# Crafted Web+Mobile Template - Summary

## Template Overview

This template implements **Hexagonal Architecture** (Ports & Adapters) with **100% shared business logic** between Next.js (web) and React Native (mobile).

## Key Characteristics

### 1. Pure Domain Layer (ZERO Dependencies)

**Location**: `/packages/domain/`

The domain layer is the heart of the application and has **ZERO external dependencies**:

- ✅ No Prisma
- ✅ No Next.js
- ✅ No React
- ✅ No React Native
- ✅ No Node.js specific APIs

**Contains**:
- Entities: `User`, `Product` (rich domain models with behavior)
- Value Objects: `Email`, `Money`, `Result` (immutable)
- Use-Cases: `CreateUser`, `GetUserById` (orchestration)
- Repository Interfaces: `IUserRepository`, `IProductRepository`
- Domain Errors: `DomainError`, `ValidationError`, `NotFoundError`

**Why ZERO dependencies?**
1. Can run on Node.js, Browser, React Native, Deno, Bun
2. 100% testable without infrastructure
3. Shared between web and mobile without modification
4. Long-term maintainability (not coupled to frameworks)

### 2. Infrastructure Layer (Implementations)

**Location**: `/packages/infrastructure/`

Implements the domain repository interfaces:

- `PrismaUserRepository` implements `IUserRepository`
- `PrismaProductRepository` implements `IProductRepository`

**Dependency Flow**:
```
Infrastructure → Domain (✅ Correct)
Domain → Infrastructure (❌ Never)
```

### 3. Application Layer (Ultra-Thin)

**Location**: `/packages/api/`

tRPC routers that orchestrate use-cases:

- Max 10 lines per procedure
- Validation only (Zod schemas)
- Error mapping (domain → HTTP)

### 4. Platform-Specific Apps

**Web**: `/apps/web/` - Next.js 15 + shadcn/ui
**Mobile**: `/apps/mobile/` - React Native + Expo + NativeWind

## Architecture Diagram

```
┌─────────────────────────────────────────────────────────┐
│                    UI Layer (Separate)                  │
│  ┌──────────────────┐         ┌──────────────────┐     │
│  │   Next.js Web    │         │  React Native    │     │
│  │   shadcn/ui      │         │   NativeWind     │     │
│  └──────────────────┘         └──────────────────┘     │
└─────────────────┬───────────────────────┬───────────────┘
                  │                       │
                  └───────────┬───────────┘
                              │
┌─────────────────────────────▼───────────────────────────┐
│              Application Layer (tRPC)                    │
│  ┌────────────────────────────────────────────────┐     │
│  │  Ultra-thin routers (< 10 lines)               │     │
│  │  - Validation (Zod)                            │     │
│  │  - Use-case orchestration                      │     │
│  │  - Error mapping                               │     │
│  └────────────────────────────────────────────────┘     │
└─────────────────────────────┬───────────────────────────┘
                              │
┌─────────────────────────────▼───────────────────────────┐
│        Domain Layer (ZERO external dependencies)        │
│  ┌────────────────────────────────────────────────┐     │
│  │  Entities: User, Product                       │     │
│  │  Value Objects: Email, Money, Result           │     │
│  │  Use-Cases: CreateUser, GetUserById            │     │
│  │  Repository Interfaces: IUserRepository        │     │
│  │  Domain Errors                                 │     │
│  └────────────────────────────────────────────────┘     │
└─────────────────────────────┬───────────────────────────┘
                              │
                              │ (implements)
                              │
┌─────────────────────────────▼───────────────────────────┐
│           Infrastructure Layer                          │
│  ┌────────────────────────────────────────────────┐     │
│  │  PrismaUserRepository → IUserRepository        │     │
│  │  PrismaProductRepository → IProductRepository  │     │
│  │  Stripe, Resend, etc.                          │     │
│  └────────────────────────────────────────────────┘     │
└─────────────────────────────┬───────────────────────────┘
                              │
                              ▼
                        ┌──────────┐
                        │ Database │
                        │ External │
                        │ Services │
                        └──────────┘
```

## File Structure

```
crafted-web-mobile/
├── .craftedrc.json                    # Configuration (level: crafted)
├── package.json                       # Monorepo root
├── turbo.json                         # Turborepo config
├── .env.example                       # Environment variables
│
├── apps/
│   ├── web/                           # Next.js 15 app (TODO)
│   └── mobile/                        # Expo app (TODO)
│
├── packages/
│   ├── domain/                        # ⭐ CORE: Pure business logic
│   │   ├── src/
│   │   │   ├── entities/
│   │   │   │   ├── User.ts
│   │   │   │   └── Product.ts
│   │   │   ├── value-objects/
│   │   │   │   ├── Email.ts
│   │   │   │   ├── Money.ts
│   │   │   │   └── Result.ts
│   │   │   ├── use-cases/
│   │   │   │   ├── CreateUser.ts
│   │   │   │   └── GetUserById.ts
│   │   │   ├── repositories/
│   │   │   │   ├── IUserRepository.ts
│   │   │   │   └── IProductRepository.ts
│   │   │   ├── errors/
│   │   │   │   └── DomainError.ts
│   │   │   └── index.ts
│   │   ├── __tests__/                 # 100% coverage tests
│   │   │   ├── entities/
│   │   │   │   └── User.test.ts
│   │   │   └── value-objects/
│   │   │       ├── Email.test.ts
│   │   │       └── Money.test.ts
│   │   ├── package.json               # ZERO external deps
│   │   ├── tsconfig.json
│   │   └── vitest.config.ts
│   │
│   ├── infrastructure/                # Implementations
│   │   ├── src/
│   │   │   └── database/
│   │   │       └── repositories/
│   │   │           ├── PrismaUserRepository.ts
│   │   │           └── PrismaProductRepository.ts
│   │   ├── package.json
│   │   └── tsconfig.json
│   │
│   ├── api/                           # Application layer
│   │   ├── src/
│   │   │   ├── routers/
│   │   │   │   └── user.ts            # Ultra-thin (< 10 lines)
│   │   │   ├── trpc.ts
│   │   │   └── index.ts
│   │   ├── package.json
│   │   └── tsconfig.json
│   │
│   ├── database/                      # Prisma
│   │   ├── prisma/
│   │   │   └── schema.prisma
│   │   ├── src/
│   │   │   └── index.ts
│   │   └── package.json
│   │
│   ├── validators/                    # Zod schemas (shared)
│   │   ├── src/
│   │   │   ├── user.ts
│   │   │   └── index.ts
│   │   └── package.json
│   │
│   ├── api-client/                    # tRPC client (shared)
│   │   ├── src/
│   │   │   └── index.ts
│   │   └── package.json
│   │
│   └── storage/                       # Platform-agnostic storage
│       ├── src/
│       │   └── index.ts               # IStorage interface
│       └── package.json
│
└── docs/
    ├── README.md                      # Main documentation
    ├── ARCHITECTURE.md                # Architecture details
    ├── HEXAGONAL.md                   # Hexagonal architecture guide
    ├── MOBILE.md                      # Mobile integration guide
    └── CLAUDE.md                      # Rules for Claude Code
```

## How Domain is Shared Across Platforms

### 1. Same Business Logic

```typescript
// packages/domain/src/use-cases/CreateUser.ts
// ✅ Works on Node.js, Browser, React Native
export class CreateUser {
  constructor(private userRepository: IUserRepository) {}

  async execute(input: CreateUserInput): Promise<Result<User, DomainError>> {
    // Business logic is 100% platform-agnostic
    const emailResult = Email.create(input.email);
    if (isFailure(emailResult)) return emailResult;

    const userResult = User.create({
      id: crypto.randomUUID(),
      email: emailResult.value,
      name: input.name
    });

    if (isFailure(userResult)) return userResult;

    return this.userRepository.save(userResult.value);
  }
}
```

### 2. Same API Layer

```typescript
// packages/api/src/routers/user.ts
// Called from both web and mobile
export const userRouter = router({
  create: publicProcedure
    .input(createUserSchema)
    .mutation(async ({ input }) => {
      const useCase = new CreateUser(userRepository);
      const result = await useCase.execute(input);

      if (isFailure(result)) {
        throw new TRPCError({ code: 'BAD_REQUEST', message: result.error.message });
      }

      return result.value.toJSON();
    })
});
```

### 3. Platform-Specific UI Only

**Web**:
```typescript
// apps/web/app/signup/page.tsx
import { Button } from '@crafted/ui/web';

export default function SignupPage() {
  const mutation = trpc.user.create.useMutation();

  return (
    <Button onClick={() => mutation.mutate({ email: '...', name: '...' })}>
      Sign Up
    </Button>
  );
}
```

**Mobile**:
```typescript
// apps/mobile/app/(tabs)/signup.tsx
import { Button } from '@crafted/ui/mobile';

export default function SignupScreen() {
  const mutation = trpc.user.create.useMutation();

  return (
    <Button onPress={() => mutation.mutate({ email: '...', name: '...' })}>
      Sign Up
    </Button>
  );
}
```

## Key Principles

### 1. Dependency Inversion

```
❌ Bad: Use-Case → PrismaRepository
✅ Good: Use-Case → IUserRepository ← PrismaRepository
```

### 2. Result Pattern (No Throws)

```typescript
// ❌ Bad: Throws in domain
export class Email {
  static create(email: string): Email {
    if (!isValid(email)) throw new Error('Invalid');
    return new Email(email);
  }
}

// ✅ Good: Result pattern
export class Email {
  static create(email: string): Result<Email, ValidationError> {
    if (!isValid(email)) {
      return failure(new ValidationError('Invalid email'));
    }
    return success(new Email(email));
  }
}
```

### 3. Rich Domain Models

```typescript
// ❌ Bad: Anemic model
export interface User {
  id: string;
  name: string;
}

// ✅ Good: Rich domain entity with behavior
export class User {
  private constructor(private props: UserProps) {}

  updateName(name: string): Result<void, ValidationError> {
    // Business rule enforced in entity
    if (!name || name.trim().length < 2) {
      return failure(new ValidationError('Name must be at least 2 characters'));
    }
    this.props.name = name;
    this.props.updatedAt = new Date();
    return success(undefined);
  }
}
```

## Testing Strategy

### Domain Layer: Spec-First (100% Coverage)

```typescript
// 1. Write test first (specification)
describe('User Entity', () => {
  it('should fail with name less than 2 characters', () => {
    const result = User.create({ id: '123', email: emailValue, name: 'J' });
    expect(isFailure(result)).toBe(true);
  });
});

// 2. Implement to satisfy test
export class User {
  static create(props: UserProps): Result<User, ValidationError> {
    if (props.name.length < 2) {
      return failure(new ValidationError('Name must be at least 2 characters'));
    }
    return success(new User(props));
  }
}
```

### Infrastructure Layer: Integration Tests (80% Coverage)

```typescript
describe('PrismaUserRepository', () => {
  it('should save and retrieve user', async () => {
    const repository = new PrismaUserRepository(prisma);
    const user = User.create({ id: '123', email: emailValue, name: 'John' });

    await repository.save(user.value);
    const result = await repository.findById('123');

    expect(isSuccess(result)).toBe(true);
  });
});
```

## What Makes This Crafted Level

| Aspect | Implementation |
|--------|---------------|
| **Architecture** | Hexagonal (Ports & Adapters) |
| **Domain Dependencies** | ZERO (100% platform-agnostic) |
| **Function Length** | Max 20 lines (domain), 10 lines (routers) |
| **Parameters** | Max 2 |
| **Duplication** | 0 tolerance (CI fails) |
| **Complexity** | Cyclomatic < 5 |
| **Test Coverage** | Domain 100%, Use-cases 100%, Infra 80% |
| **Error Handling** | Result pattern (no throws in domain) |
| **Documentation** | WHY comments + ADR references |
| **Principles** | All 16 Craft principles active |

## Benefits

✅ **Testability**: Domain testable without infrastructure
✅ **Maintainability**: Clear separation of concerns
✅ **Portability**: Domain shared between web and mobile
✅ **Evolvability**: Easy to add features without regression
✅ **Longevity**: Architecture sustainable for 10+ years
✅ **Type Safety**: End-to-end TypeScript with tRPC
✅ **Developer Experience**: Single source of truth for business logic

## Next Steps

1. Add Next.js web app in `/apps/web/`
2. Add Expo mobile app in `/apps/mobile/`
3. Add UI packages in `/packages/ui/{web,mobile,primitives}/`
4. Add auth package in `/packages/auth/`
5. Add payment package in `/packages/payments/`
6. Add email package in `/packages/emails/`
7. Complete E2E tests

## Documentation

- [README.md](./README.md) - Quick start and overview
- [ARCHITECTURE.md](./ARCHITECTURE.md) - Detailed architecture explanation
- [HEXAGONAL.md](./HEXAGONAL.md) - Hexagonal architecture guide
- [MOBILE.md](./MOBILE.md) - Mobile integration patterns
- [CLAUDE.md](./CLAUDE.md) - Rules for Claude Code

## Template Status

✅ Domain layer (100% platform-agnostic)
✅ Infrastructure layer (Prisma repositories)
✅ Application layer (tRPC routers)
✅ Validators (Zod schemas)
✅ Storage abstraction
✅ API client
✅ Configuration files
✅ Documentation
⏳ Web app (Next.js) - TODO
⏳ Mobile app (Expo) - TODO
⏳ UI packages - TODO

This template is ready for development. The core hexagonal architecture is in place with a pure domain layer that can be shared between web and mobile applications.
