First of all, i'm not talking about validation in the sense of the nullability or length of a field like in this and this questions where we can use isValid() methods in business classes or an external Validator. However, my question is related to validate or not in business classes. I'm using a layered architecture style with services consuming the datasource layer and interacting with the domain model.

Simplyfing the domain requirements, suppose I have Movies and Reviews, and Users can make only one Review per Movie. So, a second Review is a valid object but the business rules should not allow the review to be completed.

So, i could do:

ReviewService >> addReview(review,movie)
  loggedUser = getLoggedUser()
  review = reviewRepository.searchReviewFrom(loggedUser,movie)
  if (review == null)
    reviewRepository.save(review)
  else
    // some mechanism to inform the error, it could be NotificationPattern

Or:

Movie >> addReview(review)
   for r in this.reviews
     if r.user = review.user
       throw ReviewAlreadyUploaded
   // if no expcetion is thrown
   this.reviews.add(review)

I think this oneReviewPerUser validation is a pure business rule, and should go in the business classes, in this case the Movie class. Thus the business rules are contained whitin the domain and no scattered between the layers. However, if the lists of Reviews of the Movies are big in almost every case, this option implies query and instantiate a big number of Review object and iterate over all elements.

So assume that I choose option 1, I have to assure that every client that wants to create a review for a Movie must use the Service, because it may break the business constraint of 1 review per user if, for example, use directly the ReviewRepository. This starts to sound like a layer of Services with all the business logic and an AnemicModel with Domain Classes being bags of attributes for carry data to and from the database.

Where is the line that separates the business logic that should be on Services from the one that should be on Domain Classes.

In a DDD (Domain Driven Design) application using a conventional layered architecture, business logic goes into the domain layer. Now, please note this layer does not only contain the domain entities (Movie, Review), but also domain services (like a ReviewService class) and repositories. So, ReviewService is a business class as well.

Your concern about every client having to use the appropriate business method (addReview) is not realistic. There is just no practical way to avoid a poorly written client class from violating business rules, if the developers don't stick to the rules of the chosen architecture. For example, if Movie has a getReviews() method (or a reviews property) which exposes a mutable collection of reviews, client code can always add an arbitrary review. And even if it's immutable, client code can always create the invalid Review in the database by directly using a repository.

In my opinion, it's best to have only relatively simple business methods in domain entities, and usually only for read-only operations. More complex business logic is best assigned to cohesive domain service classes. Otherwise, your entity classes will quickly become too big and complex. So, for example, a business operation that adds a movie review subject to business validation rules would be best put in a ReviewMaintenance domain service class, or even a ReviewCreation class if the "add review" operation is sufficiently complex.

You are leaving out the possibility of restricting the input in the database. If there can indeed only be one review of a particular movie by a particular user, that should be a database constraint. If you do that, then your concern is how to handle a failed attempt to insert a second review -- how does that get reported to the user.

  • Yes, I leave that possibility out, first because i think it´s to late and second because exists more complex validations that can't be achieved with database constraints. In regard to if it's to late, it's arguable. Is better a query for check if exists the review plus an insert if not exists? Or is better avoid the query and call insert directly and catch de database exception? Maybe at the time yo do the insert you already done a bunch of other tasks that now you must undone, wich doesn't happen if you validate first. – gabrielgiussi Jul 13 '15 at 12:42
  • @gabrielgiussi: just because the database can't do all checks, doesn't mean that you shouldn't put in the checks that you can. As for query plus insert or insert and catch exception - I would say it's query to control the UI, and then catch exception if that fails (multiple tabs/windows/apps). – jmoreno Jul 13 '15 at 14:45
  • You're right, however my original question was oriented to more complex business rules, maybe the example wasn't the best. Database constraints for validation is another issue. Thanks for the reply. – gabrielgiussi Jul 13 '15 at 15:49

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.