I'm getting my head around DDD and how to build up an C# Project structure for an .Net Core WebApp. I searched quite a bit around the web, for example How to structure a Domain Driven Design in an Onion Architecture?, https://github.com/zkavtaskin/Domain-Driven-Design-Example and several Pluralsight courses.
What bogs me is the fact, that this HelloWorld-examples don't seem to work very well in the real life: Let's say we build the propagated onion structure of Application, Domain and Infrastructure. Application would in this case be the WebApi-part, Domain the Domain-Services, Entities, Value Objects etc. and Infrastructure the implementation of the Interfaces defined in the Domain-Assembly.
So far so good, but with this approach, I'd need to define every bit of helping code in the Domain-Layer. For Services, Handlers, etc. I could define just the Interfaces and let the implementation inject from the Infrastructure-layer. But as soon as I started coding like this, I had folders like Attributes, Handlers (low level services for REST, Files etc.) and a ton of other technical stuff in my domain-layer. For the sake of testing, I tried to revert the dependency, so the Domain targets the Infrastructure. This allows to move all the supporting code and low-level handlers to the Infrastructure, but makes communication awkward: Since the entities and value objects are still in the Domain, the Infrastructure doesn't know them anymore, so I'd need to map them to DTOs, adding another mapping layer just for this communication.
From reading Eric Evans book, I took the idea, that the Domain-Layer should be written in the ubiquitous language, be technical agnostic and be the communcation platform with the domain experts. But I can't find a good solution to have the both of best worlds. Am I missing something crucial here?
3 Answers 3
I think that we often out-think ourselves when we are trying to learn a new way of doing things. Instead of boiling the problem down to the smallest thing that can possibly work, we think about things that aren't important right now. Onion architecture and DDD work together to provide a way of letting you define that simplest way of doing things.
What's really your domain?
That's the question you need to be asking yourself. The most core concepts are:
- How domain objects interact with other domain objects
- How domain objects interact with primary services
Everything else is completely outside the domain. Whether you use a Repository pattern for persistence or something else is beside the point. There will be points where your domain object needs to interact with a service. That's OK.
- Stub your services using interfaces and domain terminology
And stop. You can have a fully unit tested domain driven design with these two concepts.
Services from the inside out
In an Onion architecture, the implementation of your services are done at a layer outside of your domain model. That keeps the domain clean, and separated from the concerns of your service.
One example would be using a SQL database for persistence. If you need to change that in the future, or add other types of databases (graph, search, etc) to facilitate your system, you can localize those changes behind the service you defined in the domain model.
Common things that are not central to the domain:
- Authentication
- Persistence
- Rendering (i.e. REST services, full web application, desktop app, etc)
- Internationalization
- Messaging
That is not a full and exhaustive list.
Your domain may have users, and those users have roles... but how the user is authenticated and those roles supplied are things that will change over time.
Modes of messaging may change over time, as well as specific content, but the types of messages and triggers that kick them off are related to the domain.
Feel free to apply the same concepts to implementing your services as you did for the core domain. The only difference is the domain for a persistence service is different than for your core application.
Project Structure
It really depends on how granular you want to go, but at the very least you should have one library as your core domain model. You can have service implementations in separate projects, or group some things together.
+ Solution
+ Project.Domain (or Core if you prefer)
+ Project.Domain.Test
+ Project.Persistence.Service
-- etc.
* WebAPI
Don't be overly religious about your project organization. It only needs to be reasonably clear where to look for things. Your core domain library has the domain objects and interfaces for services as necessary. The only rule I'd recommend is to have a clear hierarchy of project dependencies. Project.Domain shouldn't depend on anything outside of your standard library for the language you are using.
Are you familiar with Ports and Adapters? I think it might help you get the dependencies sorted out. This article is a good starting point.
http://www.dossier-andreas.net/software_architecture/ports_and_adapters.html
It's important to learn the distinction between primary and secondary Ports and Adapters if you want to apply this pattern successfully.
The links at the bottom of the article are worth reading.
-
Thanks, I even disagree 180% with the author: I think Infrastructure should be mostly known by the Application-Layer, while the Domain-Services should only need to know the Repository-Interfaces.I went ahead and renamed the Services.Domain.Infrastructure to Services.Domain.Data to reflect this idea. I will have a look, how fare I can get with this idea.Matthias Müller– Matthias Müller08/07/2017 10:08:48Commented Aug 7, 2017 at 10:08
Interfaces are declared in the Domain layer when they need to be used by the domain. From your second link :
Put your infrastructure interfaces in to Domain Model Layer. Your domain will use them to get data, it doesn't need to care how, it just knows there is an interface exposed and it will use it.
Where I seem to differ with the author is : not all infrastructure objects will be like this. I usually have many infrastructure services used only by the Application layer and don't define their interfaces in the domain.
Explore related questions
See similar questions with these tags.
TFS-Workitem
, theTFS-Workitem
(I suppose that this is an Aggregate) perform state mutation according to the business rules then theapp service
gets theTFS-Workitem
(or a DTO) and pass it to the Infrastructure to perform the REST call.