Passionate Developer

Memory is unreliable like a software, so make my thoughts more eternal and my software more reliable

DDD Architecture Summary

In this blog post you can find my general rules for implementing system using Domain Driven Design. Do not use them blindly but it is good starting point for DDD practitioners.

Bounded Context

  • Separate bounded context for each important module of the application (important from business partner perspective).
  • Independent of each other (if feasible).
  • For monolithic application separate Spring Framework context for each bounded context, e.g: applicationContext-domain-crm.xml, applicationContext-domain-shipping.xml, etc.
  • CRUD like bounded contexts (user management, dictionaries, etc.) should be implemented as Anemic Domain Model.

Domain

  • Place for application business logic.
  • Must be independent of the technical complexity, move technical complexity into infrastructure.
  • Must be independent of the particular presentation technology, move presentation related stuff into web.
  • Internal package structure must reflect business concepts (bounded contexts), e.g: crm, shipping, sales, shared, etc.

Domain Model

  • Rich model, place for: entities, domain services, factories, strategies, specifications, etc.
  • Best object oriented practices applied (SOLID, GRASP).
  • Unit tested heavily (with mocks in the last resort).
  • Unit tests executed concurrently (on method or class level).
  • Meaningful names for domain services e.g: RebateCalculator, PermissionChecker, not RebateManager or SecurityService.
  • Domain services dependencies are injected by constructor.
  • Having more than 2~3 dependencies is suspicious.
  • Entities are not managed by containers.
  • Aggregate root entities are domain events publishers (events collectors).
  • Aggregates in single bounded context might be strongly referenced (navigation across objects tree).
  • Aggregates from different bounded contexts are referenced by business keys (if feasible).
  • No security, no transactions, no aspects, no magic, only plain old Java.
  • Interfaces for domain services when the service is provided by infrastructure.
  • No interfaces for domain services implemented in the domain model itself.

Application Services

  • Orchestrator and facade for actors under Model.
  • Place for security handling.
  • Place for transactions handling.
  • Must not deliver any business logic, move business logic into domain model. Almost no conditionals and loops.
  • Implemented as transactional script.
  • No unit tests.
  • Acceptance tests executed against this layer.
  • Cglib proxied, proxy must be serialized by session scoped beans in web layer.
  • Dependencies are injected on field level (private fields).
  • Ten or more dependencies for single application service is not a problem.
  • Application services are also domain event listeners.
  • Always stateless.
  • No interfaces, just implementation.

Application Bootstrap

  • Initial application data.
  • Loaded during application startup (fired by BootstrapEvent) if application storage is empty.
  • Loading order is defined with Spring Ordered interface.
  • Data is loaded within Model API.
  • Data might be loaded within application services, e.g: load sample Excel when application is integrated with external world this way.
  • No tests, bootstrap is tested during application startup on daily basis.

Infrastructure

  • Place for technical services
  • Must not deliver any business logic, move business logic into domain.
  • Internal package structure must reflect technical concepts, e.g: ~infrastructure.jpa, ~infrastructure.jms, ~infrastructure.jsf, ~infrastructure.freemarker, ~infrastructure.jackson, etc.
  • Shared for all bounded context of the application. For more complex applications, separate technical services e.g: ~infrastructure.jpa.crm, ~infrastructure.jpa.shipping, etc.
  • Class names must reflect technical concepts, e.g.: JpaCustomerRepository, JaksonJsonSerializer, not CustomerRepositoryImpl, JsonSerializerImpl.
  • Integration tested heavily (with Spring Framework context loaded).
  • Integration tests executed by single thread.
  • Test execution separated from unit tests within test groups.
  • Separate Spring Framework context for each technical concept, e.g: applicationContext-infrastructure-jpa.xml, applicationContext-infrastructure-jms.xml, etc.
  • Separate and independent Spring test context for each technical module, e.g: testContext-jpa.xml, testContext-jms.xml, etc.

Web

  • Client specific facade (REST, MVC, JSF, etc.)
  • Place for UI logic (not applicable for JavaScript client and REST)
  • Delegates requests to application services
  • No transactions, no method level security, move security and transactions to application services.
  • No business logic, move business logic into domain.
  • Tested with mocked application services.
  • Tested with loaded spring context for MVC controllers (if applicable).
  • Serializable session scoped beans (to be safe all beans in this module should be java.io.Serializable).
  • Internal package structure must reflect UI organization structure, it might be similar to project sitemap.
  • Top level package might reflect technology or architecture e.g: presentation, rest, mvc, jsf, etc.

Comments