1

While reading "Patterns, Principles, and Practices of Domain-Driven Design" by Nick Tune; Scott Millett (certainly not the first book on DDD I've read) I started to understand usage of Martin Fowler's EAA patterns with DDD and with that there came a question.

A single SQLAlchemy model is like Active Record (if I understand it correctly), however, a set of SQLAlchemy models is very good at abstracting persistence mechanism away, so I always thought following Domain Model, degrading in some places to Anemic Domain Model.

In many cases there still are Transaction Scripts, touching several Entities. I am trying to keep these in actions / services (Service Layer?) because it feels wrong to have actions touching multiple models in the model classes themselves (circular dependencies can occur) unless they are subordinate (lay lower in the aggregate structure).

The result is not quite a pure pattern (as business logic is between "actions" and "active records") - more like some kind of hybrid of those mentioned above - but it is working in practice, captures domain knowledge (both verbs and nouns) well, fits naturally with SQLAlchemy persistence.

What pattern SQLAlchemy models (in ORM mode) naturally represent and how operations, which touch several models, can be fit into DDD way? Is there anything I can improve on the described above?

To clarify more, this question and it's answers deals with another, similar in spirit, dilemma: whether to use ORM classes directly for the domain model or add one more layer (and the favorite answer is not to overarchitect).

In this question, I am trying to find a good way to place multi-entity behaviors. My take is that "action" / "service" modules with function per action is the right place (in Python it is usual to use module level functions when there is no shared state).

  • I think you misunderstand the purpose of DDD. DDD is not a coding methodology; it is a design technique. You also seem to be misunderstanding software patterns. Patterns are not building blocks; they are well-known solutions to common problems. – Robert Harvey Jul 12 '18 at 15:51
  • On the contrary. The book I mentioned explained it well. What I am wondering about SQLAlchemy (and I guess other ORMs) seems to offer a good way, but with it's own pattern it seems. So the question is, is there something wrong as there are many recognizable patterns in the solution (inside one Bounded Context). (And SQLAlchemy at it's own level uses all those Object-Relational patterns - most of them) – Roman Susi Jul 12 '18 at 16:29
  • I guess I'm wondering why you think SQLAlchemy or Fowler's enterprise patterns have anything to do with DDD. All three of these things were authored by different people. SQLAlchemy and Fowler's enterprise patterns deal with software design from a systems perspective; DDD deals with software design from your specific business domain's perspective. – Robert Harvey Jul 12 '18 at 17:41
  • 1
    I think the point is that the fact you use SQLAlchemy is irrelevant to DDD. Your domain will be abstracted away from all these concerns. Maybe you have a repository which is implemented to use SQLAlchemy as an ORM, but your domain objects will not be tainted by this. – Vincent Savard Jul 12 '18 at 18:05
  • 1
    I'm not sure I understand what you say. The domain layer is about what your business does. You want to isolate your domain layer from irrelevant technical details such as the fact that you use an ORM. – Vincent Savard Jul 12 '18 at 18:36
4

This is a common issue, and the cause is usually related to a shallow understanding of DDD. The purpose of DDD is to model the functional requirements of your system such that the result is a useful abstraction of the core rules of your business domain. Often developers fail to understand why this is important and create a domain model almost directly mapped from their persistence model. Because data is usually a poor starting point for modeling the functional requirements of a system, this usually will result in a highly-coupled system where rules are devoid of context (and are therefore useless).

When creating a domain model, the business requirements should be the absolute focus. This often means that the most useful domain models are created before a physical model is made to store/relate/normalize the data involved. You should seek to keep your persistence layer (physical model) as loosely coupled to your domain model as possible. That's the entire point of a layered architecture right?

While I agree with the sensibilities put forth in the link you provided, it is simply a poor design choice to give your necessarily anemic persistence model (ORM) the responsibility of being your domain model. An ORM is a tool not a solution, and not something your domain should be aware of in any way, shape, or form (it needn't know nor care if/how it's persisted).

You, yourself, have already started running into the problems of mixing the two. By using your persistence model as your domain model, it is preventing you from properly modeling business processes that are dependent on data from two entities (clearly the data in this case should be combined into a single domain entity). This is precisely the reason mixing the two doesn't work. Your application is going to devolve into a system where your "models" are simply bags of data being passed around as arguments to "rules/processes". That separation of data and behavior is fundamentally procedural in nature, not OOP, and certainly not DDD.

  • This is quite radical POV. Many disagree with this pursuit of purity as impractical, eg enterprisecraftsmanship.com/2016/04/05/… , softwareengineering.stackexchange.com/questions/313188/… , stackoverflow.com/questions/14024912/… . Entity remains entity - there is almost no difference between ORM model class (per Entity in SQLAlchemy) and "pure" counterpart. Infra is almost invisible, by design. – Roman Susi Jul 12 '18 at 19:53
  • Imagine, I have a separate domain model. Where do I put multi-entity functionality then? I do not see any natural candidates in the domain itself. – Roman Susi Jul 12 '18 at 19:55
  • 1
    This is not a radical POV. It is precisely how DDD is meant to be done. Period. It's only impractical to create a multi-layered, highly-cohesive, low-coupled system if your application is trivial. As for your problem, it kind of depends what you mean by "multi-entity functionality". The simple answer is this problem would not exist. Why would I purposely split functionality between multiple entities when modeling my domain? I would essentially have to create the problem for it to exist. If you are referring to "process" instead of "data dependencies", then I would use a ProcessManager. – king-side-slide Jul 12 '18 at 20:19
  • Maybe, my multi-entity operation is a Service - "When an operation does not conceptually belong to any object." (from wikipedia on DDD). An alternative is to have a "god" entity, which certainly does not belong to the domain, at least in my case. – Roman Susi Jul 12 '18 at 20:30
  • The important bit of that quote is "any object". Are you sure you have modeled your domain correctly? That's (part of) the point I'm making above. So many questions posted here have a developer looking for some missing technical detail to solve their problem when the issue is simple: the model they have created is insufficient and is not a useful abstraction of their business rules. Be careful when implementing a domain service. Overuse of services leads to the exact same anemic model I'm warning you against above. Leaning on too many services is usually a symptom of a bigger problem. – king-side-slide Jul 12 '18 at 20:43
0

An article with lots of arguments why DM should be separate from PM (persistence model):

https://www.mehdi-khalili.com/orm-anti-patterns-part-4-persistence-domain-model by Mehdi Khalili.

Cited highlights:

  • PM is a property bag while DM is about business logic and behavior
  • Business logic is far more testable in DM than PM
  • PM looks like database while DM should look like the business domain
  • PM (typically) has dependencies on ORM while DM is POCO
  • PM has DB related constraints while DM is about business rules and constraints
  • PM can enter invalid state while DM should not allow invalid state even temporarily

(POCO - plain old ... object)

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.