3

In domain driven design

  1. 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.
  2. 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.
  3. 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 ?
16
  • 1
    Does this answer your question? Are the Repository Pattern and Active Record pattern compatible? Aug 4 at 17:11
  • 2
    The main component of the Active Record pattern is the domain model persisting itself. The Active Record pattern is more than simply querying for data. Persistence (and where this happens) is a central to the Active Record design pattern. Aug 4 at 17:24
  • 2
    I think #2 is a misunderstanding. The application should not care whether each individual entity is persisted, but the application should absolutely be aware of transactional boundaries. This implies the application layer coordinates with the persistence layer on when to "save changes", but the application does not know what changes need to be saved. That's what the persistence layer is for. Aug 4 at 17:43
  • 3
    @Ced: I'm with Greg on this, you are taking a much too literal reading of what it means to not be aware of an item being persisted. Your question acts as if persistence should be a secret that can't be revealed via a method name; which is not the criterion on which to hinge your decision. "The "when to save changes" seems to be a persistence concern too." How are you going to define a transaction in scope of everything the application layer is trying to achieve in a single atomic operation, if not by having the application layer indicate when the atomic operation is complete?
    – Flater
    Aug 4 at 18:16
  • 2
    @Ced "Maybe it's not practical which is what I'm trying to find out," You've responded a few times by excluding practical issues with your approach, such as "@RikD calling saveChanges does not obey rule 2 in this post." Either you're open to alternate points of view or you're not; but it's contradictory to at the same time claim you're trying to find out the practicality and then dismiss more practical approaches as an answer. I think you're being too dogmatic about your understanding of DDD. Purism is hardly ever the right choice in the real world. Reality drives one to make compromises.
    – Flater
    Aug 4 at 18:19

1 Answer 1

3

In domain driven design...

I'm with @Flater on this one. DDD is mainly about the domain as ubiquitous language, i.e. used everywhere, including in the code. It is less about these entity-value-repository-service things. The latter are just one interpretation even according to the book. I personally think it is not even a good one at that.

Expressing the domain is highest priority. Not technical categorization and details.

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.

That is a contradiction. Unless your domain is about CRUD (i.e. you are a database), add/remove/delete/etc. will not be part of the domain language.

An Account does not get "updated". Money gets transferred, it gets closed, etc. These operations imply persistence, but persistence is a technicality underneath.

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.

Does that sound like domain language? No it's not. It's technology. Technology should be hidden, "domain" should be visible.

With those rules in mind: How can entities updates be persisted and should the repository return self persisting entities ?

This is a strangely worded question. Should the returned objects be "self-persisting"? This is a technical question, therefore should not significantly influence the design. The real question is, does some use-case that I want to express imply persistence.

If I initiate a cash-transfer, I expect it to be persistent. Probably. If I add two Money objects I don't. Probably, depending on the domain.

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.

I agree, this is not what is implied in the book and it also doesn't make sense. Persistence is a technicality, it is not part of the ubiquitous language (most of the time).

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.

Agree again, this is pretty bad.

A third solution that I've not seen is to return a PersistedOrder instead of an order to the application layer

As mentioned in the comments, this is somewhat similar to ActiveRecords, but still misses the point entirely.

Having "records" in the first place, that is, bags of data to be retrieved and stored to a database is completely counter to object-orientation and DDD.

Database records are not domain-relevant, not part of the ubiquitous language, therefore it should not be relevant at all for design purposes.

The right alternative is to model the domain. As is. With persistence where it's implied. With UI even, where it's implied. With everything. A conversation with a domain expert / user will not stop at technical boundaries. Technical boundaries do not (again, ymmv) exist in the domain. Users will speak of Account and it's UI interchangeably.

If you're looking for a name on this, it is sometimes called "rich domain". Warning though, name not consistent, sometimes people don't mean the above when invoking rich domain.

HTH

1
  • 1
    I think flatter had a great point too against this pattern: "How are you going to define a transaction in scope of everything the application layer is trying to achieve in a single atomic operation, if not by having the application layer indicate when the atomic operation is complete?"
    – Ced
    Aug 5 at 17:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.