Domain Model pattern example

前端 未结 3 699
春和景丽
春和景丽 2021-02-01 10:20

I\'m just trying to find some examples of Martin Fowler\'s Domain Model pattern and I can\'t.

From what I found on the Internet Domain Model is just adding some \"logi

3条回答
  •  独厮守ぢ
    2021-02-01 11:21

    Did I understand it correctly? If not, I'd be grateful for a little example.

    Broadly, yes.

    From Martin Fowler, domain model is an object model of the domain that incorporates both behavior and data.
    The domain model is frequently opposed to a model where you have specific classes to bear data and some other specific classes to bear behavior/processings.

    If I take your Income class, it looks like more as a class that holds properties/data than an domain model with a real behavior.

    public boolean isPositive(){
       return amount > 0 ? true : false;
    }
    

    is a kind of utility function that has no relation with the model.
    You could put that in a Math class.

    I will try to give you a domain model example and then the version where the model separates data and processing.

    Suppose in the requirements of the domain of the application you are modeling, we need to add a bonus for incomes. This bonus may take place in winter for Christmas for example (but why not for other events)

    Rather than having a service class to do this processing, we let domain model objects perform the task.

    Incomes, a high level object could iterate on Income instances and apply the bonus and we could have a bonus rule class that defines the bonus according to some input values.
    I introduce multiple classes since the idea is to allow each objects to collaborate according to their responsibilities.

    Incomes :

    public class Incomes {
      List incomes = ...
      ....
      public void applyBonus(BonusRule bonusRule){
         for (Income income : incomes){
           income.applyBonus(bonusRule);
         }      
    }
    

    Income :

    public class Income {
    
      private float amount;
    ...
      public void applyBonus(BonusRule bonusRule){
           float bonus = bonusRule.compute(this);
           amount += bonus;
      }      
    ...
    }
    

    ChristmasRule :

    public class ChristmasBonusRule implements BonusRule {
    ...
      @Override
      public float compute(Income income){
           float bonus = ...
           return bonus;  
      }      
    ...
    }
    

    And finally, we could apply the processing in this way :

    void foo(){   
      // create a domain object that has both behavior and data
      Incomes incomes = ...; 
      // invoke a functional method on the object by passing another domain object
      incomes.applyBonus(new ChristmasBonusRule()); 
    }
    

    In a design where you separate data and logic in distinct classes, it could look like more like that :

    public class IncomeBonusService {
      // stateless : no incomes data inside it
      ....
      public void applyChristmasBonus(List incomes){
         for (Income income : incomes){
           // Christmas bonus computation here
           float bonus = ...
           income.setAmount(bonus + income.getAmount());
         }
      }
    }
    

    And we could apply the processing in this way :

    // inject the service
    @Autowired
    IncomeBonusService incomeBonusService;
    
    void foo(){       
       // create a domain object that has only data
       List incomes = ...; 
       // invoke a service method by passing data as parameter
       incomeBonusService.applyChristmasBonus(incomes); 
    }
    

    A model design where the objects have no behavior (only getter/setter) is called Anemic Domain Model.


    Big differences between the two ways illustrated by this example :

    Domain model :

    • The objects are meaningful.

    • Behavioral responsibility finely defined between classes.
      So good isolation, testability and maintainability.
      For example, adding/removing/unit-testing a BonusRule is easy.

    • Objects responsible of their state.
      Indeed, no need to provide setters as the object can itself update its state after collaborating with other objects.
      We could see that in Amount.applyBonus() :

      float bonus = bonusRule.compute(this); amount += bonus;

    Anemic Domain Model :

    • All the logic is in the service class.
      So a single place to get the code.
      With few lines, it is fine.
      But note that this advantage has a certain limit because as the logic becomes big or complex, the best thing is often splitting the logic in multiple service classes.

    • But whatever the number of Service classes you need, the whole logic is located in the service classes and not somewhere else. Which may ease development norms if we compare it to the domain model where the logic may be exploded in some different "types" of classes.

    • Necessity to provide getter/setter for domain classes.
      The domain is not responsible of its state and its invariant rules either.
      So any class that depends on the domain class can "break" its state.

    As a side note, some frameworks (for persistence, mapping, serialization, ...) rely by default on getter/setter.
    That's why this model, despite its drawbacks, leads in some projects.

提交回复
热议问题