Loading...
Loading...
[Pragmatic DDD Architecture] Guide for the overall project architecture, Domain-Driven Design (DDD) principles, Modular Monolith (Bounded Contexts) organization, and file structure. Use when deciding where to place a new file, creating a new module, or understanding the application's layers and dependency flow.
npx skill4agent add leif-sync/pragmatic-ddd architecturesrc/authfoldersshort-urlsusersworkspacessrc/app/shared/src/<bounded-context>/
├── domain/ # 1. CORE: Pure typescript. No frameworks. No DB imports.
│ ├── entities/ # Rich domain models with getters and .toSnapshot() rules.
│ ├── value-objects/ # Immutable primitives (e.g., folderName.ts) with validation.
│ ├── errors/ # Domain errors (e.g., folderAlreadyExistsError.ts).
│ └── interfaces/ # Contracts (Abstract Classes) for Repositories.
│
├── use-cases/ # 2. APPLICATION: Orchestration layer.
│ ├── createFolder.ts # Specific business flows. Uses repositories and domain models.
│ └── createFolder.test.ts # Co-located unit tests.
│
├── infrastructure/ # 3. INFRASTRUCTURE: External concerns. Database, Email, etc.
│ ├── postgresFolderRepository.ts # Implementation of domain interfaces.
│ └── postgresFolderRepository.integration.test.ts # Co-located integration tests.
│
└── presentation/ # 4. PRESENTATION: Next.js and UI concerns.
├── actions/ # Server Actions (e.g., createFolderAction.ts).
├── components/ # React Components (e.g., FolderList.tsx).
└── hooks/ # React Hooks.| Concept | Location | Naming Convention | Example |
|---|---|---|---|
| Bounded Context | | | |
| Value Objects | | | |
| Entities | | | |
| Domain Errors | | | |
| Interfaces | | | |
| Use Cases | | | |
| Infrastructure | | | |
| Server Actions | | | |
| Components | | | |
domain/use-cases/infrastructure/presentation/zoduuidneverthrowResultdomain/interfaces/src/shared/infrastructure/bootstrap.tsserviceContainerserviceContainerserviceContainer.folders.createFolder.execute(params)src/app/src/app/presentation/components/app/[locale]/app/dashboard/[locale]LocaleProviderpage.tsxlayout.tsxserviceContainersrc/shared/sharedshared/domain/ResultRepositoryErrorshared/infrastructure/bootstrap.tsserviceContainer.tsload-env.tsdrizzle-postgres/i18n/shared/presentation/shadcnLocaleProvidershared/test/txTestsrc/auth/authauth.tsauth-client.tsgetSession.tspresentation/components/templates/process.envsrc/shared/infrastructure/load-env.tsretrieveEnvVarz.ZodTypez.coerce.number().int().positive()AppUrl.from(value)._unsafeUnwrap()load-env.tsPUBLIC_export const PUBLIC_APP_NAMENEXT_PUBLIC_APP_NAMEload-env.tsload-env.tsimport { z } from "zod";
import { AppUrl } from "@/shared/domain/value-objects/appUrl";
// Base helper
function retrieveEnvVar<T>(params: { name: string; schema: z.ZodType<T> }): T {
const value = process.env[params.name];
const parsed = params.schema.safeParse(value);
if (parsed.success) return parsed.data;
throw new TypeError(`Environment variable ${params.name} is invalid...`);
}
// 1. Standard string validation
export const PUBLIC_APP_NAME = retrieveEnvVar({
name: "NEXT_PUBLIC_APP_NAME",
schema: z.string().min(3),
});
// 2. Coercion (e.g., port to number)
export const POSTGRES_PORT = retrieveEnvVar({
name: "POSTGRES_PORT",
schema: z.coerce.number().int().positive(),
});
// 3. Wrapping in a Value Object
export const PUBLIC_APP_BASE_URL = AppUrl.from(
retrieveEnvVar({
name: "NEXT_PUBLIC_APP_BASE_URL",
schema: z.url(),
}),
)._unsafeUnwrap({ withStackTrace: true });*.test.ts*.integration.test.tsinfrastructure/txTest*.ui.test.tspresentation/presentation/components/PascalCase.tsxpresentation/actions/camelCaseAction.tsZodserviceContaineruse-cases/camelCase.tsstatic from()domain/interfaces/Result<Data, DomainError>neverthrowResultassertNever().toBranded()