Avoid externalizing decisions from your domain model
I recently saw an entity that looked something like this:
public class Prisoner { public virtual bool CanBePutInIsolation() { ... } public virtual bool IsEligibleForVacations() { ... } public virtual bool CanSendToWork() { ... } }
I won’t show the logic in those methods, but it was fairly involved and very business focused. It is also, incidentally, caused my rhino sense to tingle. It didn’t surprise me to find out that the UI looked like this:
Well, the logic is well encapsulated, and it clearly is business logic. Why should I care where I am actually putting it? Isn’t the place of domain logic in the… domain?
Yes & no. The problem that I have here is that those are query methods that are only going to be used in the UI. There is no business logic in the application that actually uses them, it is only the UI that will call them to make presentation decisions.
There are actually several issues here, first, and I want to make it clear, this is not an issue of mixing presentation logic in the entities. Next, and the reason that my rhino sense tingled is the presence of a Boolean query method on the entity. Every time that I see one I become very suspicious. Boolean methods worry me because of their implications. If you have a Boolean method here, it means that somewhere else you have an if statement that works based on this method.
And at that point, you really have to ask yourself if this is an appropriate decision to make. Usually, I find, you can avoid it by moving the responsibility into the domain. But what about this case? I can’t really move the responsibility for creating the UI into the entity, after all. And somewhere in the application I must have this knowledge so I can build my UI.
I am not going to try to answer this question at this moment. I have my own thoughts about the subject, but I would like to have additional feedback about this from the community.
Comments
This is (in my opinion) the prime example for something like a View-Model based approach to UI design. By definition, it transforms stuff to be more easily represented in the UI, and is separate from the domain.
In this instance, I would probably make a Prisoner View-Model, but I'm still thinking it's a bad idea to put business logic like that into the VM. Better would be to rebuild the part of the system that needs that object, if possible, so that it doesn't have queries in the entity itself, then refactor into a VM which can then be used to format stuff appropriately so the UI can bind to, or whatever.
In previous applications I've worked on we modelled these types of things as Commands that had a CanExecute() boolean method on them. In this case I'd make a AssignToWorkCommand, for example, that took in a Prisoner. The UI could bind to CanExecute() for being enabled/disabled and when clicked the command could execute the appropriate logic on the Prisoner.
I did something similar to Jeremy. I had my class implement ISignalTarget which could create a list of valid ISignal, which had an Enabled boolean property on them.
Although ultimately I think you end up with a more elaborate version of what you already have :-)
You still have to check in business logic whether a prisoner can be isolated or not. Aren't you?
So, I think that such methods should live in domain model.
And View model (or DTO) should have corresponding properties for that.
Something like that:
public class PrisonerDTO
{
...
public bool CanBePutInIsolation;
public bool IsEligibleForVacations;
public bool CanSendToWork;
}
And when initializing this DTO you will initialize these properties from methods of domain objec.
I disagree. When you initiate the "Assign to work" or "Initiate vacation request" transaction you probably want to check if the Prisoner is still eligible for that. IMO, that check should be apart of the AssignToWork() method or whatever.
So, you have domain logic that needs to utilize those checks. Perhaps not exactly in that manner. But the query should stay close to the command that it is making available so that the validation logic can be reused.
Sebastian raises an interesting question for me. CQS says you should separate your commands and your queries. But if your SendToIsolation COMMAND lives on a service layer shouldn't it be QUERYING that you can send the person to isolation?
Well, assuming that the information isn't rapidly changing, you could abstract the notion of a prisoner action and ask the domain model for them during initialization of the screen. So you could have something like this:
public class Presenter
{
}
Where action is an implementation of the Command pattern.
We try to practice CQS so we wouldn't have those methods on our domain objects since the domain itself doesn't need them. We would instead query for the answer from the database directly and translate those results into a DTO.
The question becomes where does the business logic for the decision live? Two options I see are..
Criteria objects which Business Logic in them..
Prisoner tracks CanBeIsolated but doesn't allow access via getter/setter.
It seems to me that CanBeIsolated might require a lot of different variables. Having the Domain Prisoner track all these changes might get messy so I'd probably look at the criteria option more closely.
How about specification?
CanBePutInIsolationSpecification : Specification <prisoner
{
}
Shane, as for CQS, it's more about avoiding queries internally issue commands, than commands internally issue queries. To avoid side effects. I.e. SendToIsolation doesn't return anything so it's a command.
Take the Wikipedia sample of a command... It is "querying" X for the current value internally and incrementing it.
void increment_x()
{
x=x+1;
}
Another question to consider: will the application have more than one UI? If there is an html and desktop client, that could affect my decision about where the logic lives.
I'm liking ViewModel for this type of thing, at least for MVC apps. In this example, I would use the name [ModelName][ViewName]ViewModel. So, NOT PrisonerViewModel, but PrisonerIndexViewModel, since there may be 10 Prisoner-related view models.
The logic that builds out PrisonerIndexViewModel can live where you want- generally not in the controller, and definitely not in the View. One interesting place is in the class itself:
var model = new PrisonerIndexViewModel(prisoner);
The right location for the logic really does depend on how many UIs you have hanging from it and how large the application is.
If you want to achieve CQS, your presentation never ask entities for their state.
It will use a query service that will return DTOs containing domain state.
So no need for your entity to provide these methods.
As others have pointed out, the domain queries are specific to the presentation and the presentation should have it's own view model. I would see an object building the view model which will control the view.
public class PrisonerViewModel
{
}
class PrisonerViewModelBuilder
{
}
Given components A and B where A depends on B, you don't want B to be designed to meet A's every whim and fancy. In such a case, Visual Studio may perceive a 1-way dependency, but in fact there is a 2-way dependency in design. I think this is one of the arguments against certain uses of the repository pattern, right? That is, repositories too often look up in the stack to the UI layer to see what it wants.
Instead, ORM tools (like NHibernate) only look down to the store layer to see what must be done to abstractify interactions with the DB for any hypothetical app. More generally, B should remain ignorant of A, both in code and in design. If you need another set of abstractions tailor-made for A, you can have those as a well-defined part of A, resulting in high cohesion and low coupling.
I don't see a problem here with the way it is implemented. And most of the solutions to the "problem" presented here seem to overly complicate the whole system for the sake of what?
And I also agree with Sebastian. Domain logic would be using these methods -- like in the AssignToWork method which would need to make sure the entity still CanSendToWork.
I asked Oren few days ago where should I put rules since I have Domain Objects and DTO's. He answered me to make real logic in Domain and just copy the results to DTO's properties. This is similar to what peter is suggesting here but it didn't solves all cases
I have a more problematic scenario where I really need to put the logic in UI and in Domain.
One example would be that in UI I want to simulate and in Domain I really want to do it. If I do it twice I have to do it exactly the same and alway update both. If I do it in a common place I end up putting it in my Utils class. And Oren talks about this here:
ayende.com/.../...y-util-amp-common-libraries.aspx
In my case I wanted to simulate payments conditions.
Cassio,
I don't get it. There should never be any reason to duplicate logic, or do any real business logic, in your UI directly. What you're suggesting points to a large hole in your domain that is just missing - it might be that the business just doesn't recognize it's missing (very possible), or that a mistake was made.
Without knowing a lot more about what domain you're talking about, it sounds more like you need a domain object that can do a Simulate and a Commit function - these call to a private method on the domain object that does the same thing, only the second one actually commits the change to the appropriate place. Of course, this is kind of assuming an ActiveRecord-like approach, which it may not be, but you should give more details if you need better answers. :)
I think you are asking for a request message like RequestVactionForPrisoner(). In the UI you can decide to give it a try but the request may not succeed because there are conditions (like "did escape during last vaction") that should be checked in the domain model to prevent you from doing something stupid. Entering data into the DB without validation is always a bad idea.
Yours,
Alois Kraus
Alois,
Though I like your approach, I don't believe it solves the original problem posed. If you read carefully the problem has to do with the fact that he wants to disable the actions not allowed for this entity.
In that case I would make it two staged. First request vacation which does only return the state if the action is possible. When we click on the enabled button then we try to execute the action a second time which we commit immediately to ensure that changes in the DB represent the most up to date data. If you let the UI e.g. two days alone and then commit a vacation request it could fail miserably in reality due to changed constraints.
class Logic
{
}
class Response
{
public bool CanCommit;
public void Commit();
}
So we have
public void UpdateButtonsForPrisoner(Prisoner p)
{
}
public void OnClickRequestForVacationButton(Prisoner p)
{
}
Like alot of others, the decision whether a prisoner can go on a vacation does sound like domain logic to me and not an UI decision. The UI only decides how to represent this information (disabled buttons, invisible buttons, error message when you click the button).
That makes a lot of sense Sebastian and follows in line with how I work.
I can feel the problem; all those GUI-friendly methods makes an object more of a GUI whore than a hard-core-business-logic-unit.
I've seen the problem before, and also wondered how to approach it.
Like others, my inkling is to introduce a DTO/view model/mediator/adapter/decorator or memento; one of these can be the responsible spokesman who presents or extends the object in a useful way for the GUI.
What's annoying is that all these solutions mean adding more stuff. I'd want to find a way of adding a "GUI informer" that keeps the domain clean. Do we need a new paradigm ;)
Hi Kyle. Sorry if was not clear enough.
I will not duplicate the code and I don't have functions called Simulate or Commit.
I will try to explain with a simpler code:
public class DomainFoo
{
}
public class DTOBar
1) Let's suppose in UI I want to show the MixedDescription after user inform the description. - This is what I called simulate
2) I don't have a DomainFoo object to make it to me in UI
3) I don't want to duplicate the code to generate MixedDescription in DTOBar - As you pointed
4) I have to put this function somewhere else.
What I was saying is that I wanted to let id in the domain but I can't because I'll need to use it in UI. In some cases, where I don't find a good place to share, I have to put it in some kind of Util class, what Oren said is a bad pattern and I agreee.
Maybe there is a pattern that I don't know to better solve this problem.
I think the UI never should make any descisions by it self, because any decision is based on a specific business logic. In my apps every Button starts a specific UseCase and every UseCase is based on a UseCaseDefinition. The UseCaseDefinition has a collection of Rules attached and the availability of a UseCase is checked against those rules by a UseCaseBuilder, based on the current context in wich the UseCase should be run (here the prisoner). Since these rules are injected it is easy to extend the RuleSet with no further dependencies. And since any Rule can use any service, there is no need to duplicate functionality. So if you have for instance an other button "Can receive visitors" you have no need the change the entity model by providing a new method CanReceiveVisitors, wich is for my understanding the main problem in the above sample.
I would argue for a presentation model that sits between the Domain Model and UI. This additional layer has the necessary logic to query the domain model and interpret domain information and put it in a presentation friendly format.
For example, in the Domain Layer there may be a LastParoleReviewDate but for the purposes of the UI we just need a flag on the page ParoleReviewedInLastSixMonths. The Presentation Layer is responsible for this. This doesn't contaminate the Domain Model and can be easily tested.
For things like CanSendToWork(), I like Jeremy's suggestion of creating Commands. So SendToWork is a meaningful Command it does exactly what it says on the packet and a method of CanExecute() makes it obvious where to find the permissions to use that command. Again, this is easily testable.
As a final note, methods like CanBePutInIsolation and IsEligibleForVacations do not sound like methods of Prisoner but are more in keeping with a service class that is related to the prisoner and/or the prison depending on the business case.
I think that all of those things ARE domain logic. They are pieces of information about a prisoner, rather than information on HOW to display a prisoner. I would make those methods properties, though, and have them calculated early.
If you still want those properties separated from the Prisoner class, make a one-to-one class off of Prisoner called PrisonerVacationRequestEligibility and put them there. I'd still have them calculated early, though. This way they are easily discoverably, simple to understand and explain, and very performant.
Have to say that the specification (or query object) pattern makes the most sense here.
Why? Having the IsEligibleForVacations property (with lots of business logic) on the domain model means you can't query your domain propertly. It means, for example, you can't ask it to give you all prisoners who are eligible for vacations, without doing a massive in-memory filter of all prisoners (not ideal!). The domain model should not assume how the application wants to use it.
I think this shows an example of why the separation between the presentation and domain makes things a lot harder than they should be. In my mind, the presentation is part of the domain, at least the part of the presentation which is supposed to show data. If I build a banking application, a "Total Amount in Account" is shown in the presentation because it is an important piece of information for somebody interested in the "banking domain". In this sense, the presentation is driven by the domain (the total amount in an account must be accessible from the domain model) and the domain is driven by the presentation (this is a piece of information a user will want to view, so the domain must know how to handle it).
Naturally, this is a two-way relationship, so I don't see a reason to separate this functionality into two separate layers. Now, if you are talking about the code that actually puts this data onto the screen, I consider that a presentation concern which should be separate. But if you are just talking about being concerned about putting properties that are only meant to be viewed (and not part of some other domain calculation) in the "domain layer", I say that this I something that should be in the domain layer. The only thing that should be in the presentation layer should be logic which knows how to put data onto the screen...what goes on the screen (ie what is important to the user of your prisoner domain) and how this is calculated is a natural part of the domain layer.
Mike
Off topic: What tool do you use to draw your diagram?
@Brad - www.balsamiq.com
I think ViewModel/DTO is definitely needed and there's no question about it. But the problem in question is more on where to place the logic to acquire that information in the first place before we can even provide it to ViewModel.
Anyway, IMO these specifications are validating the eligibility of certain actions. So it should live whereever the action takes place....
Hence, if SendToIsolation is a domain operation, then by all mean, IsEligibleForVacations should live there, or somewhere else within reach. (This knocks presentation and app layers off the list)
I see a strategy pattern developing - we have the domain object and a set of behaviors associated to that domain object.
+1 on the specification pattern suggested above.
Personally I like Ritesh Rao's blog post on the subjects of validation/business rules:
www.codeinsanity.com/.../...tion-and-business.html
"developing implementations of Validation and Business Rules logic in your applications, using Expressions and Specifications as light weight rule evaluators."
I would utilize the specificaiton pattern for this scenario. encapsulate it away in the business layer.
Use service(s) objects that do something for you instead being something. Eventually this business question is going to get complicated and the user is going to want to see the broken rules etc.. the service will be able to provide the information without mucking up your domain.
public class CanBePutInIsolationService: IService
{
.
.....
....
.}
Comment preview