领域驱动设计批判

时间:2011-03-31 15:37:28

标签: domain-driven-design

我想对下面的域驱动设计提出一些建议和批评。我在下面包含了伪代码。真正的代码将具有封装属性。

的关注

我唯一担心的是它似乎是贫血。

的步骤

  1. 使用值
  2. 创建新请求
  3. 是否已获得批准?
    1. 如果是,请显示值
    2. 如果不是,请说明未批准的原因
  4. enum UnitedStatesState {
      ALABAMA,
      //...
      CALIFORNIA,
      //...
      MAINE,
      //...
      WASHINGTON
    }
    
    class License {
      int id;
      String name;
    
      //enum of state that the license is applicable in
      UnitedStatesState state; 
    }
    
    class LicenseRequest {
      //the name of the person making the request
      String name; 
    
      //enum of state to which the user is requesting a license in
      UnitedStatesState state; 
    
      LicenseResponse submit()
      {
         //TODO: move creation of the rules out of this class
         RuleGroup<LicenseRequest> ruleGroup = new RuleGroup<>();
         ruleGroup.add(new StateExclusionLicenseRequestRule(UnitedStatesState.MAINE));
    
         boolean approved = ruleGroup.execute(this);
         if(approved) {
           License license = createLiscense(request);
           return new ApprovedLicenseResponse(license);
         } else {
           DeniedLicenseResponse response = new DeniedLicenseResponse();
           response.rules = newArrayList(ruleGroup);
           return response;
         }
      }
    
      //TODO: move create license out of Request. maybe a factory class?
      private License createLicense()
      {
         License license = LicenseIdGenerator.generate(this.state);
         license.name = this.name;
         license.state = this.state;
         save(license);
         return license;
      }
    }
    
    //visitor for the rule
    interface Rule<T> {
      public boolean execute(T o);
      public List<String> getMessages();
    }
    
    //rule that auto denies when the request is made in an excluded state
    class StateExclusionLicenseRequestRule : Rule<LicenseRequest> {
      public List<String> getMessages();
      UnitedStatesState excludedState;
      public boolean execute(LicenseRequest request) {
         if(request.state == excludedState)
         {
           messages.add("No license for " + request.state + " is available at this time.");
           return false;
         }
         return true;
      }
    }
    
    //rule that groups all other rules
    class RuleGroup<T> : Rule<T> {
      public void addRule(Rule<T> rule);
      public List<Rule<T>> getFailedRules();
    
      public List<String> getMessages() {
         List<String> messages = new ArrayList<>();
         for(Rule<T> rule : rules) {
           messages.addAll(rule.getMessages());
         }
         return messages;
      }
    
      public boolean execute(T o) {
         List<Rule<T>> failedRules = new ArrayList<>(rules.size());
         for(Rule<T> rule : rules) {
           boolean approve = rule.execute(o);
           if(!approve) {
             failedRules.add(rule);
           }
         }
         return !failedRules.isEmpty();
      }
    }
    
    interface LicenseResponse {
      boolean approved;
    }
    
    class ApprovedLicenseResponse : LicenseResponse {
      License license;
    }
    class DeniedLicenseResponse : LicenseResponse {
      private List<Rule<LicenseRequest>> rules;
    
      public List<String> getMessages()
      {
         List<String> messages = new ArrayList<>();
         for(Rule<LicenseRequest> rule : rules) {
           messages.addAll(rule.getMessages());
         }
         return messages;
      }
    }
    

    示例代码

    request = new Request(name: 'Test', state: UnitedStatesState.CALIFORNIA)
    response = request.submit()
    if(response.approved)
    {
      out('Your request is approved');
      out('license id = ' + reponse.id);
    }
    else
    {
      out('Your request was denied');
      for(String message : response.messages)
      {
        out(message);
      }
    }
    

    更新1:背景

    这只是对我想要实现的内容的嘲弄。这是一个简单的系统,用户将信息输入到关于他们自己的表单中,并且他们被批准或拒绝许可。批准后,可以打印证书。

    为了举例,唯一的规则是拒绝在缅因州申请许可证。

    更新2:重构规则和删除处理程序

    我对上面的例子做了一些修改。删除了Handler并将所有代码移动到LicenseRequest。我还将批准/拒绝的规则移到了实现vistor模式的类中。

2 个答案:

答案 0 :(得分:2)

不幸的是,没有显示一些更相关的代码,但我会查看可以将哪些代码推送到LicenseRequest。特别是,LicenseRequest可能会创建License而不是处理程序(可能通过为其提供ID)。如果LicenseRequest的属性仅用于创建已批准的许可证,则尤其如此。然后这些不必暴露于吸气剂。

我也有determineApproval(可能有另一个名字)直接创建响应,而不是传递可写消息列表(仅用于失败)。

你应该寻找的气味是Feature Envy。特别是,应检查使用LicenseLicenseRequest数据的任何计算,以确定是否应在这些类中进行计算。

数据对象(特别是不可变数据对象)有一个目的,但您应该关注它。

答案 1 :(得分:0)

状态应该是可扩展性的ValueObjects。例如,当您需要将州的缩写与其名称等同时会发生什么?

看起来您的设计确实没有聚合根。如果没有许可证,LicenseRequest是否毫无意义?在这种情况下,它应该通过许可证(或许可证服务)处理,而不是直接由调用代码(可以是Web服务器,控制台应用程序等)处理。

因此伪代码会像

var license = licenseFactory.NewLicense();
response = license.Request(name, state)

更有意义吗?如果您拥有多种类型的许可证,例如商业驱动程序,会发生什么?

此外,对于规则而不是命令模式(执行),您可以通过将规则作为规范来获得更清晰的解决方案 - https://en.wikipedia.org/wiki/Specification_pattern