Skip to main content

Use DDD Framework to build the data-access project

Problem Statement​

Create a code framework:

  • that is easier to maintain and test.
  • that can scale and evolve the system over time.
  • that aligns with the terminology used by domain experts and users.

Decision Drivers​

  • Reduced Complexity: DDD breaks down complex business logic into smaller, more manageable pieces (aggregate roots, entities, value objects, etc.).
  • Easier Mocking: DDD's emphasis on interfaces and abstraction makes it easier to mock dependencies, reducing the complexity of testing.
  • Separation of Concerns: DDD separate Business Logic from Infrastructure layer, making it easier to test the business logic without worrying about the underlying infrastructure.
  • Ubiquitous Language: DDD promotes a common shared language between developers, domain experts and users, thereby avoiding ambiguity/confusion in communication.
  • Anti-Corruption Layer: DDD provides a way to isolate the domain layer from external systems, reducing the impact of changes in external systems on the domain layer.

Decision​

We will implement a DDD framework using the following components:

  • Domain Layer: This layer will contain the domain models and business logic to manage them.
  • Application Service Layer: It serves as an intermediary between the user interface and the domain model.
    • coordinates use cases and fulfill application requirements.
    • orchestrates the domain model to execute business logic and retrieve/update data.
    • implements business logic that doesn't naturally fit within the domain model.
    • transaction management and other cross-cutting concerns.
  • Infrastructure Service Layer: It handles interactions with external systems or services, such as third-party APIs or data stores.
    • facilitates communication and translation between the application and external systems
    • implements anti-corruption layer to translate data and operations between the domain model and external systems using adapters/converters.

Domain Layer​

Context​

It defines the scope of a model and ensures that terms and concepts are consistent within that scope.

  • Think of it as a microservice boundary, where each microservice has its own domain model and language.
  • Implementation: src/app/domain/contexts/*
  • Example: community

Entity​

An entity is an object within the domain that has a unique identity and is identifiable throughout its lifetime. An entity may have attributes and relationships with other entities.

  • Think of it as a real-world object that can be uniquely identified by an Id.
  • Implementation: src/app/domain/contexts/**/*.ts [..maybe, add '.entity.ts' extension]
  • Example: community, role, member, account

Entity Props​

An interface for attributes or characteristics of an entity. It defines the state of the entity at any given time.

Entity Reference​

An interface that governs how an entity refers to another entity. It could be a readonly version of the Entity Props interface, allowing the calling entity to access the referred entity's state but not modify it.

Value Object​

Objects that are equal due to the value of their properties, are called value objects. They are immutable and can be shared across entities.

  • Think of it as a compound attribute set where individual attributes don't have any meaning on their own, but together they represent a concept, like price = 5 USD where price is the value object consisting of amount and currency attributes
  • Implementation: src/app/domain/contexts/**/*.value-objects.ts
  • Example: community, account, profile (Typically, one for each entity, with classes for individual value objects such as address, payment)
  • @lucaspaganini/value-objects: provides framework to create and validate value objects.

Aggregate-Root​

An entity that acts as a guardian for a group of related entities (known as an aggregate). It is responsible for maintaining the integrity and consistency of the aggregate and for enforcing business rules.

  • Think of it as an entry point to access and manage a group of related entities.
  • Implementation: src/app/domain/contexts/**/*.ts [...maybe, add '.aggregate-root.ts' extension]
  • Example: member (aggregate-root), managing the accounts, custom views, role, etc.

Unit of Work​

It ensures that a set of changes to the domain model are treated as a single, atomic operation. If one part of the operation fails, the entire operation is rolled back, ensuring data consistency.

  • Think of it as a transaction boundary for data persistence.
  • Implementation: src/app/domain/contexts/**/*.uow.ts
  • Example: : member (Typically, one for each aggregate-root.)

Repository​

An abstraction that provides access to the aggregate root, encapsulating the details of data storage and retrieval. Repositories make the system more modular and easier to test.

  • Think of it as an interface that provides CRUD operations for aggregate root, typically a save and multiple purpose-specific get methods.
  • Implementation: src/app/domain/contexts/**/*.uow.ts
  • Example: : member (Typically, one for each aggregate-root.)

Consequences​

  • We will need to invest time in learning and implementing DDD principles and patterns.
  • We will need to ensure that our domain models are correctly defined and separated from the infrastructure.
  • We will need to use interface adapters to connect our application layer to our infrastructure layer.

More Information​