General design principles
- Adhere to the Single Responsibility Principle (SRP), i.e. every module (class/method) has one responsibility, not several.
- Choose names from the business domain for classes, methods, attributes, parameters and variables.
- Use domain objects as parameters instead of primitives like string or int.
- Design the system such that domain classes can be unit tested individually.
- Each (part of a) sub system should use one design metaphor, either
- domain centered - business logic placed in domain objects, preferable when changes to state is most important - or
- service centered – business logic placed in stateless services that operate on data in DTO:s, preferable when the data flow is most important.
From this follows
- Keep business logic and technical implementation details separate, i.e. do not combine these two types of complexity.
- Adhere to Command Query Separation (CQS), i.e. each method should be either a command (having a side-effect and possibly returning a result) or a query (returning a result without any side-effect).
On a more detailed level
- As a tool for communication use the following stereotypes for objects implementing the business domain logic:
- Value Objects – immutable, created when needed, not persisted.
- Entities – mutable (as per allowed by business rules), loaded/persisted through Repositories.
- Repository – interface describing load/persist of Entities.
- Factory – interface describing creation of Entities and Value Objects when external dependencies (to Repositories, Factories or Services) must be fulfilled.
- Service – stateless class housing business logic that operate on data in DTO:s
- Dependencies are preferably handled through dependency injection to facilitate loose coupling between objects.
Application of principles
These principles define a solid approach to design of new (sub) systems. When used in work with an existing code base it can be used for continuously applying small improvements in accordance with “the boy scout rule” and as vision to compare with current state in order to form a road map for larger refactoring efforts.