I've read about DDD for days now and need help with this sample design. All the rules of DDD make me very confused to how I'm supposed to build anything at all when domain objects are not allowed to show methods to the application layer; where else to orchestrate behaviour? Repositories are not allowed to be injected into entities and entities themselves must thus work on state. Then an entity needs to know something else from the domain, but other entity objects are not allowed to be injected either? Some of these things makes sense to me but some don't. I've yet to find good examples of how to build a whole feature as every example is about Orders and Products, repeating the other examples over and over. I learn best by reading examples and have tried to build a feature using the information I've gained about DDD this far.

I need your help to point out what I do wrong and how to fix it, most preferably with code as "I would not recomment doing X and Y" is very hard to understand in a context where everything is just vaguely defined already. If I can't inject an entity into another it would be easier to see how to do it properly.

In my example there are users and moderators. A moderator can ban users, but with a business rule: only 3 per day. I did an attempt at setting up a class diagram to show the relationships (code below):

This question came from our site for professional and enthusiast programmers.

From the Wikipedia article: " Domain-driven design is not a technology or a methodology. " thus the discussion of such is inappropriate for this format. Also, only you and your 'experts' can decide if your model is right.
–
starbolinJun 20 '12 at 22:04

1

@Todd smith makes a great point on "domain objects are not allowed to show methods to the application layer". Note the first code sample the key to not injecting repositories into domain objects is, something else saves and loads them. They don't do that themselves. This lets the app logic control transactions, too, instead of the domain/model/entity/business objects / or whatever you want to call them.
–
FastAlJun 25 '12 at 13:25

2 Answers
2

This question is somewhat subjective and leads to more of a discussion than a direct answer, which, as someone else has pointed out - isn't appropriate for the stackoverflow format. That said, I think you just need some coded examples on how to tackle problems, so I'll give it a shot, just to give you some ideas.

The first thing I'd say is:

"domain objects are not allowed to show methods to the application layer"

That is simply not true - I'd be interested to know where you have read this from. The application layer is the orchestrator between UI, Infrastructure & Domain, and therefore obviously needs to invoke methods on domain entities.

I've written a coded example of how I would tackle your problem. I apologise that it's in C#, but I don't know PHP - hopefully you will still get the gist from a structure perspective.

Perhaps I shouldn't have done, but I have slightly modified your domain objects. I couldn't help feel it was slightly flawed, in that the concept of a 'BannedUser' exists in the system, even if the ban has expired.

To start with, here is the application service - this is what the UI would call:

Pretty straight forward. You fetch the moderator doing the ban, the user who the moderator wants to ban, and call the 'Ban' method on the user, passing the moderator. This will modify state of both the moderator & user (explained below), which then needs persisting via their corresponding repositories.

The invariant for a user is that they cannot perform certain actions when banned, so we need to be able to identify if a user is currently banned. To achieve this the user maintains a list of serving bans that have been issued by moderators. The IsBanned() method checks for any serving bans that are yet to expire. When the Ban() method is called, it receives a moderator as a parameter. This then asks the the moderator to issue a ban:

The invariant for the moderator is that it can only issue 3 bans per day. Thus, when the IssueBan method is called, it checks that the moderator doesn't have 3 issued bans with today's date in its list of issued bans. It then adds the newly issued ban to it's list and returns it.

Subjective, and I'm sure someone will disagree with the approach, but hopefully it gives you an idea or how it can fit together.