Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DDD - Entity creation and validation responsibility

I'm interested in DDD (Domain Driven Design) in the last days, but I can't figure out the responsibilities of who creates and validates the entities. Ill break this questions to cover different scenarios.

  1. Regular entity (Possibly with value object). As an example lets take a user who is identified by Email. I have a UserFactory that receives an array of data (possibly from form POST), and return me a new UserEntity. Should the factory validate the integrity of the data (ex: string given as Email is a real email, passwords in password field 1 and field2 match and etc)? Should the factory validate that no such user exists already (we dont want to register two users with the same email)? If yes should it do it my itself or using the UserRepository?

  2. Aggregate entity. Lets assume we have a Post entity and Comments entities. I want to get post 12 with all its comments, so I do something like

    $post = $postRepository->getById(12);

How getById should be implemented? like this:

public function getById($id) {
    $postData = $this->magicFetchFromDB($id);
    $comments = (new CommentRepository())->getForPost(12);
    return new PostEntity($postData, $comments);
}

Or maybe the post responsible for lazy creating its comments, something like:

class PostEntity {
    public function getComments() {
        if(is_null($this->_comments)) $this->_comments = (new CommentRepository())->getForPost($this->_id);
        return $this->_comments;
    }
}

? I'm very lost here and there is not enough information with examples for DDD in PHP, so any help will be appreciated!

Thank you a lot, skwee.

like image 671
Dmitry Kudryavtsev Avatar asked Feb 06 '12 14:02

Dmitry Kudryavtsev


People also ask

How are domain models validated?

Implement validations in the domain model layer. Validations are usually implemented in domain entity constructors or in methods that can update the entity. There are multiple ways to implement validations, such as verifying data and raising exceptions if the validation fails.

How are entities related to domain-driven design DDD )?

A domain entity in DDD must implement the domain logic or behavior related to the entity data (the object accessed in memory). For example, as part of an order entity class you must have business logic and operations implemented as methods for tasks such as adding an order item, data validation, and total calculation.

What is DDD example?

An aggregate is a domain-driven design pattern. It's a cluster of domain objects (e.g. entity, value object), treated as one single unit. A car is a good example. It consists of wheels, lights and an engine.

What is context of validation?

Messages in Context Validation. Messages is an object that represents the validation state builder. It provides an easy way to collect errors, warnings, and information messages during validation. Each Messages object has an inner collection of Message objects and also can have a reference to parentMessages object.


2 Answers

  • The factory should only care about creating entities. I personally preferr to put validation both in my views and in the model layer. I'd use some library like jQuery's validation plugin to make some essential validation on the client side (like checking if required fields have data). And then do the "hardcore" validation on the model. I do this is by using a simple BaseEntity abstract class which all entities extend, and since you asked for an example, here it is:

    abstract class BaseEntity {
        public function isValid();
    }         
    
    class MyEntity extends BaseEntity {
         public function isValid() {
             //actual validation goes here
         }
     }
    

    You could also make use of a static helper class with some basic validation methods:

    class ValidationHelper {
        public static function isValidPhonenumber($value) {
            //check valid phonenumber, using a regex maybe
        }
    
        public static function isAlphanumeric($value) {
            //check for letters and numbers only
        }
    }
    

    Many argue against static methods because they can break unit tests, but in this case they are pretty basic and they don't have external dependencies, thus making them "safer".

  • When it comes to checking for already existing entities you can do this by querying your DB to see if the entity is already there before adding/updating, or (this is how I like to do it) you could add a unique index to those columns that can't be repeated in the DB, and then wrap the create or update queries in a try-catch block (the query will throw a unique constraint violation, if two users have same e-mail for example) and then show the proper error message

  • On your last question it comes down to a matter of preference. If your DB will get a million hits in 1 minute, then it's probably better to use lazy loading to avoid fetching unnecesary data until it's needed. But if your database is relatively small you could perfectly use eager loading without sacrificing too much performance. Again, this is a matter of personal preference.

hope some of this rambling makes sense, cheers!

like image 55
jere Avatar answered Oct 19 '22 17:10

jere


The best and easiest way for this is to use Doctrine2. Maybe the first hours will be hard, but once you master the Doctrine2 all such relationships and aggregate are piece of cake.

You can find plenty of information about PHP & DDD or Doctrine2 on http://giorgiosironi.blogspot.com/ or just by googling.

RE: Validation - considering the Single Responsibility Principle we use Validation objects. Validation can be complex and may require other repositories or entities so it is better to keep it apart from the actual entity - subject of the validation to avoid creation of bloated objects. You can either use visitor or specification design pattern.

There is plenty other posts here regarding those topics - try to use the above keywords.

like image 43
Tomas Dermisek Avatar answered Oct 19 '22 18:10

Tomas Dermisek