Is this leaking domain knowledge to service layer?

83 Views Asked by At

Currently, my service layer handles loading aggregate roots that are responsible to react to some domain events. This involves calling the persistence layer to filter and load the responsible aggregate roots using some domain knowledge (who should/when to react). Is this considered domain knowledge leakage and how to prevent it?

Thank you!

1

There are 1 best solutions below

0
VoiceOfUnreason On

my service layer handles loading aggregate roots that are responsible to react to some domain events. This involves calling the persistence layer to filter and load the responsible aggregate roots using some domain knowledge (who should/when to react).

If you are willing to substitute "application layer" for "service layer", I think you'll find that's a pretty good match for the patterns that Eric Evans describes in the original Domain Driven Design book.

This layer is kept thin. It does not contain business rules or knowledge, but only coordinates tasks and delegates work to collaborations of domain objects in the next layer down. It does not have state reflecting the business situation, but it can have state that reflects the progress of a task for the user or the program.

The sample cargo shipping application (a collaboration between Eric Evans and Citerus) exhibits patterns that I see fairly often when discussing designs with other DDD practitioners. The code I think you are talking about is here: https://github.com/citerus/dddsample-core/blob/master/src/main/java/se/citerus/dddsample/application/impl/BookingServiceImpl.java

  public void assignCargoToRoute(final Itinerary itinerary, final TrackingId trackingId) {
    final Cargo cargo = cargoRepository.find(trackingId);
    if (cargo == null) {
      throw new IllegalArgumentException("Can't assign itinerary to non-existing cargo " + trackingId);
    }

    cargo.assignToRoute(itinerary);
    cargoRepository.store(cargo);

    logger.info("Assigned cargo " + trackingId + " to new route");
  }

In this case, Itinerary and TrackingId are value objects - the definitions are in the domain layer, but these instances are actually constructed in the web/presentation layer, and passed to the application logic that is responsible for coordination.


I am wondering how to go about loading/filtering the responsible aggregate roots without leaking domain knowledge to service layer? For example, filtering aggregate roots using some business knowledge about who/when should a domain event be handled.

The heuristic I use is that we want to separate calculation/logic from information retrieval. So if identifying the right aggregate to load depends on the form data submitted by the user, a tax table, and the conversation the CEO had at the airport last week, then we'd prefer a design like

aggregateId = computeTheAggregateId(formData, taxTable, ceo.conversation())

aggregate = repository.get(aggregateId)
aggregate.doSomethingCool(formData)

Does computeTheAggregateId belong in application or in domain? In the abstract, it's not obvious that it matters very much. The right answer might depend on how often it changes, or what code changes at the same time.

Ultimately: this is a pattern language - if the pattern doesn't "work" in the context you are in, then you are expected to have the good judgment not to use it.