Project Structure Guide
Introduction
This page outlines common approaches to organizing software projects, especially when applying Domain-Driven Design (DDD) principles. It explains the purpose of each type of component and where it belongs in the folder hierarchy.
High-Level Organization
Domain: Core business logic and rules.
Application: Orchestration, DTOs, and use cases.
Infrastructure: Technical details like persistence, middleware, and external services.
Presentation: API endpoints, UI, or CLI.
Domain Layer
Domain Services: These are stateless services that encapsulate domain logic which doesn't naturally fit within an entity or value object. They represent operations or business rules that involve multiple entities or aggregates.
Implementation vs Interface: Domain Services are typically defined as interfaces in the domain layer to express the contract and domain intent. They represent the domain's business operations without technical details. The concrete implementations of these interfaces usually reside in the infrastructure or application layers, where technical concerns like persistence, external system calls, or messaging are handled.
Example: An interface like IPaymentProcessingService might be defined in the domain to represent payment logic, while the actual implementation that interacts with a payment gateway lives elsewhere.
Entities: Objects defined by identity (e.g., Order, Customer).
Value Objects: Immutable types defined by attributes (e.g., Money, EmailAddress).
Aggregates: Clusters of entities and value objects with a root entity. An aggregate is a consistency boundary that ensures all changes within it are atomic and consistent. The root entity controls access and lifecycle of the aggregate.
An aggregate is a group of related objects in a domain model that are treated as a single unit to keep data consistent. It has a root entity called the aggregate root, which controls all changes inside the aggregate to ensure everything stays correct and synchronized. This means when you change something in the aggregate, you do it through the root entity so the whole group remains consistent.
For example, imagine an Order aggregate:
The Order is the aggregate root.
It contains multiple OrderLine entities representing items.
It may include value objects like Address or Money for shipping and pricing.
All changes to the order and its lines go through the Order root to keep the whole order consistent.
Domain Services: Stateless operations that don't fit into entities or value objects.
Application Layer
DTOs (Data Transfer Objects): Simple carriers of data between layers.
Use Cases / Application Services: Coordinate domain logic for specific scenarios.
Infrastructure Layer
Repositories: Abstract persistence and mimic collections of aggregates.
Factories: Encapsulate complex creation logic for aggregates/entities.
Middleware: Handle cross-cutting concerns like caching, logging, or authentication.
External Integrations: Clients for APIs, messaging systems, or databases.
Presentation Layer
Minimal APIs / Controllers: Define endpoints and map requests to application services.
UI Components: Web pages, views, or CLI commands.
Suggested Folder Layout
ProjectRoot/
Domain/ ** Entities/ ** ValueObjects/ ** Aggregates/ ** Services/
Application/ ** DTOs/ ** Services/
Infrastructure/ ** Repositories/ ** Factories/ ** Middleware/ ** Integrations/
Presentation/ ** Api/ ** Ui/
Key Principles
Organize by concept, not by technical detail.
Keep domain logic pure and free from infrastructure concerns.
Use consistent naming (plural for collections, singular for value objects).
Avoid catch-all folders like Utils or Common.
This structure helps ensure clarity, maintainability, and discoverability for collaborators and future maintainers.
Entities: Objects defined by identity (e.g., Order, Customer).
Value Objects: Immutable types defined by attributes (e.g., Money, EmailAddress).
Aggregates: Clusters of entities and value objects with a root entity. An aggregate is a consistency boundary that ensures all changes within it are atomic and consistent. The root entity controls access and lifecycle of the aggregate.
An aggregate is a group of related objects in a domain model that are treated as a single unit to keep data consistent. It has a root entity called the aggregate root, which controls all changes inside the aggregate to ensure everything stays correct and synchronized. This means when you change something in the aggregate, you do it through the root entity so the whole group remains consistent.
For example, imagine an Order aggregate:
The Order is the aggregate root.
It contains multiple OrderLine entities representing items.
It may include value objects like Address or Money for shipping and pricing.
All changes to the order and its lines go through the Order root to keep the whole order consistent.
Domain Services: Stateless operations that don't fit into entities or value objects.
Application Layer
DTOs (Data Transfer Objects): Simple carriers of data between layers.
Use Cases / Application Services: Coordinate domain logic for specific scenarios.
Infrastructure Layer
Repositories: Abstract persistence and mimic collections of aggregates.
Factories: Encapsulate complex creation logic for aggregates/entities.
Middleware: Handle cross-cutting concerns like caching, logging, or authentication.
External Integrations: Clients for APIs, messaging systems, or databases.
Presentation Layer
Minimal APIs / Controllers: Define endpoints and map requests to application services.
UI Components: Web pages, views, or CLI commands.
Suggested Folder Layout
You can see an example in my repository. Where to put stuff is pretty important to me as it is the key to be able to repeat the patterns and keep maintainability.
ProjectRoot/ Domain/ Entities/ ValueObjects/ Aggregates/ Services/ Application/ DTOs/ Services/ Infrastructure/ Repositories/ Factories/ Middleware/ Integrations/ Presentation/ Api/ Ui/
Key Principles
Organize by concept, not by technical detail.
Keep domain logic pure and free from infrastructure concerns.
Use consistent naming (plural for collections, singular for value objects).
Avoid catch-all folders like Utils or Common.
This structure helps ensure clarity, maintainability, and discoverability for collaborators and future maintainers.