Current Architecture Analysis¶
The project is a Purchase Request System (PRS) built with Node.js, Fastify, PostgreSQL, and Sequelize. It follows a layered architecture with some elements of Domain-Driven Design, but there are several areas where it deviates from DDD best practices.
Current Structure¶
Layers¶
The codebase is organized into the following layers:
- Domain Layer (
src/domain/): - Contains entities and constants
- Primarily consists of validation schemas using Zod
-
Lacks behavior and domain logic
-
Application Layer (
src/app/): - Contains services, controllers, and utilities
- Most business logic resides here
- Controllers handle HTTP requests and responses
-
Services orchestrate business operations
-
Infrastructure Layer (
src/infra/): - Contains repositories, database models, and logs
- Implements data access and persistence
-
Manages external resources and integrations
-
Interface Layer (
src/interfaces/): - Contains routes and API endpoints
- Defines the API contract
- Handles routing and middleware
Dependency Injection¶
- Uses Awilix for dependency injection
- Container is defined in
src/container.js - Components are registered and resolved at runtime
- Promotes loose coupling between components
Repository Pattern¶
- Uses a
BaseRepositoryclass that all repositories extend - Repositories handle database operations and are in the infrastructure layer
- Provides common CRUD operations
- Specific repositories extend the base with domain-specific queries
Entity Validation¶
- Uses Zod for schema validation
- Entity schemas are defined in the domain layer
- Provides type safety and validation
- Ensures data integrity
DDD Issues in Current Implementation¶
1. Anemic Domain Model¶
The domain model is anemic, meaning it lacks behavior and business logic:
- Domain entities are primarily data structures with validation schemas
- Business logic is mostly in services rather than in domain entities
- Entities lack behavior and methods
- Domain objects don't enforce invariants
Example from the codebase:
2. Infrastructure Leakage¶
The domain layer has knowledge of infrastructure concerns:
- Domain entities are tightly coupled with Sequelize models
- Domain layer has knowledge of infrastructure concerns
- No clear separation between domain and persistence models
- Database concerns leak into the domain layer
3. Missing Aggregates and Bounded Contexts¶
The codebase lacks clear aggregate roots and bounded contexts:
- No clear aggregate roots or bounded contexts
- Relationships between entities are defined at the database level, not the domain level
- No explicit transaction boundaries
- No clear ownership of related entities
4. Missing Value Objects¶
The codebase uses primitive types instead of value objects:
- Primitive types are used instead of value objects for domain concepts
- No encapsulation of domain concepts like Money, Email, Address, etc.
- No validation or behavior for value types
- Loss of domain semantics
5. Missing Domain Events¶
There's no evidence of domain events for communication between bounded contexts:
- No domain events for communication between bounded contexts
- No event-driven architecture
- No clear way to handle cross-domain concerns
- Tight coupling between domains
6. Business Logic in Services¶
Business rules are implemented in services rather than in the domain model:
- Business rules are scattered across services
- Domain logic is not encapsulated in domain objects
- Services contain both orchestration and domain logic
- Difficult to enforce invariants
Example from the codebase: