领域驱动设计 用DDD重构会计凭证生成(下)( 二 )


其实对于微服务的拆分可以借鉴CMMI或者的分级方式 , 将拆分效果分为三个等级 , 第三等级是雪中送炭 , 第二等级是锦上添花 , 第一等级是多此一举 , 如果考虑到上述介绍的产生了负面效果的拆分实例 , 那么其实还可以再加一个等级 , 即第零等级:画蛇添足 , 或者也可叫做画虎不成反类犬 。
除了不合理的服务拆分外 , 还有代码中的各种坏味道 。类似下面这样魔数满天飞的代码已随处可见 。
if ("0".equals(vo.getCompanyArea())//境内&& "1".equals(vo.getCompanyTaxPayerNature())) { //一般纳税人if ("1".equals(vo.getSupplierArea())) {//供应商为境外return "1";} else { //供应商为境内if ("1".equals(vo.getSupplierTaxPayerNature())) { //供应商为一般纳税人return "0";} else if ("2".equals(vo.getSupplierTaxPayerNature())) { //小规模//需判断发票类型if ("1".equals(adCreateFeeRequestVO.getInvoiceType())) { //增值税专用发票return "0";}}}}
建立凭证
重构之前 , 单据审批与凭证生成是共用同一个模型 , 模型的修改必然会同时影响这两个核心功能 , 且事实也是如此 。因此 , 采用DDD的战略工具--  , 将它们划分开 , 一个专注于单据 , 一个专注于凭证 。下文中主要介绍的是凭证限界上下文的重构过程 。
凭证生成规则代码化
在《用DDD(领域驱动设计)重构会计凭证生成(上)》 曾提到过 , 由于一个凭证生成规则需要在系统中的两个页面进行配置 , 因为配置涉及到具体代码中的一些细节 , 因此这项配置“凭证生成规则”的工作仅能由开发人员完成 , 而一个规则具体应该配置成什么样则是由产品人员提出 。如果这个规则可以由产品自己进行配置 , 那么设计成页面配置形式是没什么问题的 , 但很遗憾 , 由于在页面中的配置涉及到具体代码(通过反射替换配置项中的占位符) , 产品人员是无能为力的 。既然只能由开发人员来配置 , 且页面配置完毕后还得修改代码 , 并在完成测试后待下班时间发布上线 , 那么这种页面配置形式对于这个系统的核心功能有什么意义呢?
个人认为 , 页面配置形式要么用于在系统无需发布或重启的情况下使配置立即生效 , 随即改变系统行为;要么可以让系统的使用者通过自行配置改变系统当前行为 , 比如列表组件可以选一页最多能显示多少条记录 。对于上述两点 , “凭证生成规则”以页面形式进行配置的方式均不符合 。因此决定首先从凭证生成规则下手 , 丢弃掉既要在页面中配置 , 又要修改后台代码这种不伦不类的设计 , 将数据库中记录形式的凭证生成规则转化为代码形式 。这样不仅可以减少数据库访问次数 , 同时也提升了系统的可维护性 。
经统计 , 数据库中的凭证生成规则共计55条 。如果数据库中每一条规则对应一个类的话 , 就需要55个类 , 虽然每个类的代码量不大 , 但确实会造成类膨胀 。起初决定用分包的方式处理所创建的规则类 , 但这种方式并没有从根本上解决类膨胀的问题 。经过进一步分析发现 , 凭证类型具有非常好的稳定性 , 因此决定以会计凭证类型作为维度来定义规则类 , 然后在具体的规则类中进一步区分具体单据类型的规则 。虽然每个类中的代码相较于独立定义规则类略有增加 , 但这种设计方案使规则类的数量从55个降到了7个 。与单据时常要新增类型不同 , 会计凭证的类型十分稳定 , 这种稳定是由会计学这门学课的发展所决定的 , 这也是选择以会计凭证类型作为规则类创建维度的原因 。不过要是基于上述设计 , 那么在新增规则时就需修改规则类 , 而不是创建一个新的规则类 。从表面上看这样的设计有悖于OCP , 但实质上并没有 。可以先看下“应付凭证”这个规则类是如何定义具体生成规则的 。