What Is A Domain Service?
We’ve already discovered the difference between Data Transfer Objects (DTOs) and Domain Entities. DTOs are simple bags of publicly accessible data. They have little or no logic. On the other hand, domain entities encapsulate business rules. They also hold data, but they do not allow direct access to it. The internal data of a domain object can only be altered indirectly—via its public methods. This approach ensures that the domain entity maintains internal data consistency.
There are another set of objects that help manage the business logic of an application: Domain Services.
While Domain Entities support business rules by maintaining their own internal state, Domain Services manage the state between Domain Entities. Domain Entities are consistent by themselves. Domain Entities are (or should be) unaware of Domain Services. Domain Services corral and manage Domain Entities.
OK, let’s look at some examples of Domain Entities and Domain Services:
- ShoppingCart – This is my standard example of a typical Domain Entity. Inward oriented, a ShoppingCart object models the operation of a bona fide eCommerce shopping cart: it remembers the items in the cart and calculates a running total of the cart price.
- Customer – Among other responsibilities, this Domain Entity could maintain a count of currently outstanding customer orders, details on shipping or delivery address for completed orders.
- PaymentMethod – An entity maintaining payment method particulars in a secure manner, e.g. credit card details.
- OrderPaymentUseCase – A business logic workflow orchestrating the interaction between ShoppingCart, Customer and PaymentMethod domain entities to pay for the contents of the ShoppingCart instance and then arrange for the fulfilment of the order.
Other Domain Service examples:
- Sha256PasswordHasher – Unsurprisingly, when given a password, will this via the SHA-256 hashing algorithm.
- BankTransactionImporter – A high-level service to import bank transactions via one or more bank API clients and then invokes another service, BankTransactionRepository, to save those transactions in a persistence medium.
A few notes on Domain Services:
- One Domain Service may reference and utilise other, more refined Domain Services.
- I like to give my domain services specific names rather than call them ending in ‘Service’, e.g. CustomerService. Domain Services should be cohesive and have a specific function to fulfil.
- When designing Domain Services, it’s usually a good idea to avoid retaining volatile internal state between calls. If we don’t follow this rule and make our Domain Service is stateful, we can cause hard to find bugs. For example, if our Sha256PasswordHasher internally stores the last hashed plaintext password, we have created an exploitable security hole.