Thu Jan 22, 2004 1:08 pm
Hi,
I have this situation where I want to add a user to the system. The
question is, does the Controller check the user isnt already in existence,
or does the Repository have this responsibility?
Thanks,
NickRobinson
> I have this situation where I want to add a user to the system. The
> question is, does the Controller check the user isnt already in existence,
> or does the Repository have this responsibility?
The underlying goal appears to be to reject duplicates. If "no duplicate
users" is a domain rule -- that is, it would apply for an application in
this domain -- then I would give that responsibility to the model. The
Repository might be the most logical place in the model to enforce that
rule.
When coding the Controller, there are two options:
try {
repository.add(user);
return new CommandResult("ok");
}
catch (DuplicateUserException e) {
return new CommandResult("user exists", user);
}
|
or
if (repository.contains(user)) {
return new CommandResult("user exists", user);
}
else {
repository.add(user);
return new CommandResult("ok");
}
|
I prefer the latter because I dislike using exceptions for reasonable
conditions.
JBRainsberger
Ji JB,
Thanks for the reply. In your design (the second one) it seems the
controller is responsible for deciding on the existence, and then dealing
with this accordingly (as per the second choice). This is what I have
written, but the repository at the moment no longer checks for the existence
before calling the Add, pushing this logic into the controller. Are you
saying that even though the Repository might be a worthy place, you would
prefer to put the logic in the controller in this case?
Thanks,
NickRobinson
If the controller enforces your data integrity constraints, some other
piece of code might corrupt the repository by going around the
controller straight to the repository. If the repository does it itself,
the repository is protecting it's contents. That makes the code easier
to reason about because everything that concerns the data is together.
JohnDCorbett
This is actually what I was originally thinking when I posted my message.
My current implementation does the second option that JB presented. So as
you say, unless the controller is used, someone might attempt to add an
existing user. However, at the end of the day the backend is a database,
and this would ultimately disallow duplicate entries. But I dont like that
reasoning, in so much as it causes me to detract from the domain and
consider underlying technology.
Thanks John,
NickRobinson
As I mentioned with regards to data validation a couple weeks ago, I'd
recommend doing the logic both at the datastore level (to ensure that
your data does not get corrupted) and also at the application level
(since it should be the responsibility of the application to deal with
expected error conditions like this).
However, you might want to be careful with doing the existence check
before creating the new object. In general, I agree that it's better to
avoid using try-catch blocks to deal with expected circumstances, but
this one might end up becoming a performance problem down the road --
you are guaranteeing that in the expected case (new user), you're
performing two database roundtrips, rather than only one. I say that
this is a guarantee because even if you use a persistence framework (or
write a persistence framework) that does a good job with caching to
prevent extraneous database roundtrips, the expected case is that the
user is new, so does not exist in the db, so does not exist in the
cache. So, when you do the contains check, the cache will be consulted,
the check will fail, the database will be consulted, the check will
fail, and the new record will be inserted. In the try-catch situation,
you never incur additional DB load, and you only incur a performance
penalty if the (expected) exceptional case occurs.
Of course, this might well be a bit of premature optimization, but in
case it isn't, and you decide to go the if-else route, I'd suggest that
you ensure that this bit of functionality is encapsulated so you could
easily switch if you see more database traffic than you want etc.
Also of course, this is relatively unlikely to be a bottleneck, since
user creation is probably not on the critical path. However, the same
logic applies for other data creation situations.
PatrickLinskey
I think it depends. If in the domain, in general, it makes no sense to
allow duplicate users, then the Repository ought to protect itself
against the duplication. Allowing or not allowing duplicates may be an
application-level, rather than a domain-level, decision. If
application-level, then the Controller ought to make that decision; if
domain-level, then the Repository ought to make that decision.
Even so, the Controller /could/ ask the Repository first whether
addUser() is going to fail, just to avoid having the Repository do
something untoward, like throwing an exception or returning a bad
reference (null). Still, if someone /insists/ on doing something that
the Repository shouldn't allow, then the Repository must not allow it.
The ways to signal "not allowed" are various: null, exception, do
nothing, .... Pick one.
JBRainsberger
ControllerOrRepositoryResponsibility is mentioned on: ThreadView