3

Questions: Should I turn my dependencies around and have the DomainModel depend on Core (which holds the systems interfaces and high level policy)?

Do we ever consider a rich domain model as representing implementation details?

Background: So I have my DomainModel in a separate project with no outgoing dependencies. All of my subsystem services are in separate projects, and none of them know about each other. I also have a Core project which holds all of the system specific things such as interfaces, enums, validation classes, extensions and other things common to all the services which I wouldn't classify as low level implementation details. Core has no outgoing dependencies except to the DomainModel, all of the services depend on Core, and also DomainModel.

My current thoughts: So I've heard design statements such as

"the domain model should have no outgoing dependencies"

and potentially in contrast

"high level policy shouldn't depend on low level implementation details"

and

"modules should depend on each other in the direction of stability"

I find as I develop the system the implementation of the domain model changes quite a lot, all of the business rules and objects are there, I wouldn't consider most of the domain model stable at all, possibly some of it such as the ValueObject(s).

The advantage I see in having DomainModel depend on Core is that the high level policy then knows nothing of the implementation details of the domain model, which is also isolated from the rest of the system. Indeed, every other project would then just depend on Core and still know nothing of each other. When I make a change in DomainModel (which I often do as I develop) to build would recompile just that project, rather than the entire solution as is the case at present.

The disadvantage I see in achieving this extra amount of loose coupling is an increase in complexity? I would have to create interfaces for every single domain model object which would live in a special folder within Core. For those interfaces I'd also want to drop the 'I' prefix and have say MoneyType : Money [interface]. I imagine the only way I could then instantiate domain objects would be via abstract factories so I'd need a few more of those as well. Right now I do like being able to just instantiate concrete domain objects 'anywhere' in the system, almost like an extension to, or abstraction over, the BCL (which is part of the point of a domain model is it not?).

I can imagine the system working with either architecture, so is this just a classic case of competing design concerns - or am I completely missing something?

Edits: This highly voted answer seems to indicate that the domain model should be isolated with each object only exposing an interface to the rest of the system.

https://stackoverflow.com/a/821300/7417812

At the moment this feels better to me, as the rest of the system doesn't have unfettered access to the domain objects (thus the recompiling of them all every time there is a change). However, turning this dependency around for complete isolation will be no trivial task so I'm very open to more discussion and answers.

More edits: So I attempted the refactoring, at the point where I began trying to implement my own simple DI container I decided things were starting to become smelly and slightly ridiculous.

Any perceived benefit of complete DomainModel isolation was being offset with an exponential increase in complexity and lines of code (at least for me). So I refactored the architecture around so that DomainModel only depends on Core which now only holds my design by contract classes, custom collections, annotations and common extensions I use. I took all the system specific interfaces and infrastructure out into a new project Infrastructure which all the subsystem services depend on (no specific technologies in here so probably not a typical infrastructure layer?).

Everything feels better now however I'm back to depending on the concrete domain objects which I'm fine with. It wasn't at all a wasted exercise as I identified several improvements and simplifications of the DomainModel, and was able to clarify Core and identify the opportunity for Infrastructure.

2 Answers2

4

In the Onion architecture, one of the architectures that aligns well with DDD, the Domain layer can be further split in two sub-layers:

  1. Core: contains the building blocks not specific to any domain or technology, containing generic building blocks like lists, case classes and actors. It will never include technological concepts, e.g. REST or databases.
  2. Domain: the actual domain layer where all business logic resides with classes and methods named using the ubiquitous language for the domain. By controlling the domain through the API and putting all business logic into the domain the application becomes portable, all technical bits can be extracted without losing any business logic.

So, you could have a Core component that is shared between bounded contexts but I don't think that this should necessarily be in a separate project. It depends on how you want to propagate changes to this component to the other projects.

About the dependencies, the Domain sub-layer will depend on the Core sub-layer as it uses its low level classes but this does not break the Dependency inversion principle as both are in the same layer. However, the (entire) Domain layer should not depend on any other layers like Application, Presentation or Infrastructure, the Domain layer should remain pure, with no side effects and no dependencies. This rule should be respected in any architecture.

Constantin Galbenu
  • 16,951
  • 3
  • 38
  • 54
  • I like it, at this stage this is exactly the approach I'm working towards. Core and Domain Model feel like they belong in the same layer and not depending on anything else. Since I spent time separating out the application agnostic DomainModel it wouldn't be hard to flip the dependencies in either direction between Core and DomainModel, I feel like Core should define the API that DomainModel plugs into, all subsystems/services then depend on Core. Also, by implementing a DomainModel API it also provides the opportunity to more cleanly implement design by contract/Code Contracts too. – Christopher Sellers Jun 20 '17 at 06:46
  • Yes, but please note that the two sub-layers are from the Domain layer, they follow the Domain layer rules: pure, no side effects, technology agnostic, no IO, no external calls etc – Constantin Galbenu Jun 20 '17 at 06:47
  • Yes exactly. This reminds me of something I read regarding a 'Functional Domain Model' too. All of my value objects are immutable, in fact I work hard to make everything immutable and read only where possible. – Christopher Sellers Jun 20 '17 at 06:49
  • But you don't have to make every thing immutable. No no. Only Value objects. Entities should be mutable but they don't persist themselves. A mutable object that cannot persist itself is like an immutable one: it has no side effect; `immutability` is not a DDD requirement for domain layer, `side effect free` is – Constantin Galbenu Jun 20 '17 at 06:53
  • Yes exactly, that's how it's working. Immutable entities and aggregates don't make sense. – Christopher Sellers Jun 20 '17 at 07:01
  • @ConstantinGALBENU That's interesting. Core doesn't exist in the [original Onion article](http://jeffreypalermo.com/blog/the-onion-architecture-part-1/) which states *"remember that the Domain Model is the very center, and since all coupling is toward the center, the Domain Model is only coupled to itself"*. Do you have concrete C# examples of what you would put in the core and why? – guillaume31 Jun 20 '17 at 07:32
  • I found this `Core` sub-layer in the link from my answer and I also found in my projects that this kind of sub-layer can emerge. As an example, I have the `GuidList` as a list of GUIDs; examples of behaviors are the intersection, union and comparing of lists of GUIDs. In `PHP` we don't have this kind of classes so we must implement them by ourselves. I suppose that nor PHP neither C# provides all that a Domain layer needs as language constructs in order to be fully implemented. – Constantin Galbenu Jun 20 '17 at 07:39
  • On little comment: In DDD, the term `Core` refers in general to the `Core domain`. This answer **do not use** that meaning of the term. – Constantin Galbenu Jun 20 '17 at 07:41
  • I see. I never really needed to share that kind of thing between layers, but YMMV. Sometimes we have NuGet packages that are used in different applications, but they are rarely referenced from the Domain layer. In any case, I find it odd to call it `Core`, since these are essentially utility classes. – guillaume31 Jun 20 '17 at 07:57
  • @guillaume31 Yes, that's why I commented right before your last comment. – Constantin Galbenu Jun 20 '17 at 07:58
1

I also have a Core project which holds all of the system specific things such as interfaces, enums, validation classes, extensions and other things common to all the services

You're describing agnostic code constructs and patterns which don't inherently belong in a particular layer.

  • Interfaces and enums, as long as they are about domain concepts, should be in the domain layer. Other interfaces and enums... well, wherever they fit best.

  • "Validation classes" is also very vague, but domain validation goes in the domain, command validation in Application layer, user input validation in the UI, etc.

  • Extension methods can be found in any layer.

I think you should get rid of that "Core" project that doesn't look really coherent and fan out all its content to the appropriate projects.

IMO, everything that is "high level policy" should go in Domain. Utility classes shared by all projects you could put aside in a library-like project or NuGet package, but there shouldn't be too many of these and I wouldn't call it Core anyway.

guillaume31
  • 13,738
  • 1
  • 32
  • 51
  • There's definitely a lack of coherence with the Core project yes, some of the classes therein could definitely be distributed out however there are a few which are used by all projects and also I find holding all of the interfaces there makes it really easy to keep all of the subsystem projects separate... I'm not sure that putting the interfaces into the domain model project is the solution either because that's where I started... – Christopher Sellers Jun 19 '17 at 21:13
  • `I'm not sure that putting the interfaces into the domain model project is the solution` - That's what the [original](http://jeffreypalermo.com/blog/the-onion-architecture-part-1/) Onion approach suggests though (I mean, for domain interfaces). Some domain interfaces are not even in the centre of the Domain but slightly to the edge : *The first layer around the Domain Model is typically where we would find interfaces that provide object saving and retrieving behavior*. – guillaume31 Jun 20 '17 at 07:39
  • `I'm not sure that putting the interfaces into the domain model project is the solution` - well, at least it solves your dependency problem, right? – guillaume31 Jun 20 '17 at 07:42
  • 1
    I agree that doesn't feel like the correct solution. I wouldn't call it a 'problem' that I have, more a search for optimal system quality - like so many things in software development it may just end up being a compromise between competing concerns... – Christopher Sellers Jun 20 '17 at 07:55