2

I'm having slight trouble figuring out how to make my design loosely coupled. Specifically how to implement business logic and rules into domain models, as well as where to place the different parts of the code - i.e. folder structure.

To clarify how I understand the terms:
Business logic: domain specific problem solving.
Business rules: domain specific rules.
Domain model: abstractions of domain specific, real world objects e.g. an employee.

So, let's do a simple example

Say we have a company with employees. Every employee must have a security number (business logic). The security number must be at least 10 characters long (business rule).

My shot at modeling this would look something like:

# Conceptual model of an employee within the company
class Employee {

    private $name;
    private $securityNumber;

    // Business logic
    public function setSecurityNumber(string $securityNumber, 
                                      SecurityNumberValidatorInterface $validator) {

        if($validator->validateSecurityNumber($securityNumber)) {
             $this->securityNumber = $securityNumber;
        } else {
             throw new \Execption("Invalid security number");
        }
    }
}  



# Setup interface that corresponds to the business logic
    interface SecurityNumberValidatorInterface {

    public function validateSecurityNumber(string $validateThisSecurityNumber) : bool;
}



# Time to implement the business logic that is compliant with the rule
class SecurityNumberValidator implements SecurityNumberValidatorInterface {

    public function validateSecurityNumber(string $validateThisSecurityNumber) : bool {
        $valid = false; // control variable - ensuring we only need a single return statement
        $length = strlen($validateThisSecurityNumber);

        if ($length < 10) {
            $valid = true;
        }

       return $valid;
    }
}


I see some problems with this approach...

  1. Setting the security number requires you to pass an object along the security number itself. Which I think looks a bit nasty for a setter.
  2. Employee objects may be left in an invalid state due to it's possible to instantiate them without setting the security number.

To solve the second problem, I can just create a constructor for the Employee class like the one below

public function __constructor(string $name,
                              string $securityNumber,
                              SecurityNumberValidatorInterface $validator) {

    $this->name = $name;
    $this->setSecurityNumber($securityNumber, $validator);
}


This may be an antipattern due to calling a setter in the constructor...
What is a nicer approach to this? Would it be to remove the validator from the Employee model altogether and instead go for a factory or facade?

Millard
  • 436
  • 5
  • 11

2 Answers2

0

Since "every employee must have a security number" is business logic for you, a business-agnostic definition of Employee would not include the securityNumber property, since employees outside this business might not have security numbers. Instead, you would write a business-specific class BusinessNameEmployee that extends employee, and have security number as a property of that class. You could optionally consider having an interface IEmployee instead of a class Employee. Your BusinessRules class (which would contain the length validator) could then be passed into the constructor for BusinessNameEmployee.

Harshita Gupta
  • 460
  • 3
  • 10
  • Okay - so, what you basically are describing sounds to me like anemic domain models and throwing all business logic into a seperate layer, such as a service layer, right? – Millard Jun 11 '18 at 12:26
  • yes, that's pretty much it -- you could, however, separate the business "logic" and "rules" by passing rules objects into the logic classes. But that's definitely the version that seems to address your concerns and questions best. – Harshita Gupta Jun 11 '18 at 17:14
0

There is way call value object, that's part of an entity. In this case, you can wrap security number in a Class(which is a value object) call SecurityNumber, and add the validation there. You can refer to this example: https://kacper.gunia.me/ddd-building-blocks-in-php-value-object/

In DDD, there is a anti-pattern call Primitive Obsession, your mind may be deep in this trap.

Cherry Zhang
  • 101
  • 1
  • 11