In domain driven design
- A repository is a collection like "interface" that hides data source access. It furnish add, remove and retrieval method just like a collection would. It does it using domain language.
- The application layer uses the repositories by thinking about them as collection, by retrieving from it or adding items to it, but should not be aware of whether a specific item is being persisted or not.
- domain layer should guard domain rules
With those rules in mind: How can entities updates be persisted and should the repository return self persisting entities ?
Example:
Domain
enum Status { awaitingApproval, accepted, declined } class Order { Status _status; // private Status get status => _status; // point 3. Protects domain rules set status(Status updatedStatus) { if (status != awaitingApproval) { throw 'cannot update a $status order'; } status = updatedStatus; } } Application
// aggregate root class User { OrderRepository _ordersRepository; Future<List<Order>> viewAllOrders() => _ordersRepository.selectAll(); // point 2. the application layer is not aware of whether the order update is persisted or not. Future<void> acceptOrder(Order order) => order status == Status.accepted; } Repository
class ConcreteOrderRepository { Future<List<Order>> selectAll() async => inMemoryStorage.selectAll('orders'); // point 1. No update method on a collection like object. } There is still something missing here: How is the update to the Order being persisted ?
I've seen two version to resolve this architectural issue:
- add an update method the the repository which has as side effects:
- You can't think of the repository as a collection, it is now an abstraction over persistence. This is imo a very distinct concept of the original intent of repositories.
- The application layer is now aware it is using persistence as it has to explicitely call
orderRepository.update(order)..
- The second solution I've seen is to use an ORM framework and adding a bunch of annotation in the domain layer.
@Entity()comes to mind. I believe this is the worst solution of the bunch as it polutes the domain layer.
A third solution that I've not seen is to return a PersistedOrder instead of an order to the application layer:
class PersistedOrder extends Order { final OrderDataSource orderDataSource; factory fromOrder(OrderDataSource dataSource, Order order) { return PersistedOrder(dataSource, status: order.status) } @override status(Status status) { super.status = status; orderDataSource.updateOrder(super.id, this); } } class ConcreteOrderRepository { final OrderDataSource orderDataSource; Future<List<Order>> selectAll() async { final allOrders = orderDataSource.selectAll(); return allOrders.map((order) => PersistedOrder.fromOrder(order)); } } This seems to be in line with point 1, 2 and 3. Only the Persistence layer knows that the Order is in fact a PersistedOrder, other layers just use an order and an orderRepository as a collection where they can retrieve and add elements.
Still, somewhat of a red flag for me is that I've not seen anyone do this, so there might be a reason. Which is what this question is about:
- Have anyone encountered this pattern ? Does it have a name ?
- What would be / are possible draw backs here ?