设计能力(一)

说说概要设计

概要设计是一个设计师根据用户交互过程和用户需求来形成交互框架和视觉框架的过程,其结果往往以反映交互控件布置、界面元素分组以及界面整体板式的页面框架图的形式来呈现。这是一个在用户研究和设计之间架起桥梁,使用户研究和设计无缝结合,将对用户目标与需求转换成具体界面设计解决方案的重要阶段。

概要设计的主要任务是把需求分析得到的系统扩展用例图转换为软件结构和数据结构。设计软件结构的具体任务是:将一个复杂系统按功能进行模块划分、建立模块的层次结构及调用关系、确定模块间的接口及人机界面等。数据结构设计包括数据特征的描述、确定数据的结构特性、以及数据库的设计。显然,概要设计建立的是目标系统的逻辑模型,与计算机无关。

你如何划分领域边界

#【领域驱动设计】浅谈聚合的划分与设计

聚合以及聚合根是领域驱动设计中的重要概念,根据定义,聚合是针对数据变化可以考虑成一个单元的一组相关的对象。聚合使用边界将内部和外部的对象划分开来。每个聚合有一个根。这个根是一个实体,并且它是外部可以访问的唯一的对象。根可以保持对任意聚合对象的引用,并且其他的对象可以持有任意其他的对象,但一个外部对象只能持有根对象的引用。如果边界内有其他的实体,那些实体的标识符是本地化的,只在聚合内有意义(参见《领域驱动设计-精简版》第42页)。从定义上看,貌似针对特定上下文的领域模型来讲,聚合的划分与设计并不那么困难,但事实却并非如此。在本文中,我将大致总结一下自己的经验,同时也欢迎关注领域驱动设计的朋友能够提出自己的见解。

#聚合划分与设计其实与并发和事务性并不矛盾

首先需要了解的是,合理地划分和设计聚合,并不会产生任何并发和事务性问题。我们所讨论的文章中之所以第一个设计方案会出现并发和事务性问题,就是因为它的聚合设计本身就不合理。这其实在本文一开始就明确了这个问题:聚合是针对数据变化可以考虑成一个单元的一组相关的对象。因此,必须承认对于一个聚合,其中包含的所有对象必须“同生死,共存亡”,基于聚合的数据操作应该就是原子操作,基础结构机制需要保证以聚合为单位的数据一致性。换句话说,聚合在数据一致性方面的表现,应该与基础结构机制所保证的并发和事务的正确性是等价的。数据访问时出现的事务失效现象,其实是源于聚合的不合理划分。比如,在《Effective Aggregate Design》一文中的例子里,事实上 Product 并不一定要依赖于 Release 才能存在,因此,在 Product 的聚合中,就不应该包含对 Release 的引用,然而相反,Release 是没法脱离 Product 而单独存在的,因为如果是这样的话,Release 也就失去了本身的含义,所以,Release 可以定义成一个聚合,而 Product 则是这个聚合中的一个实体。

至此,我们可以得知,聚合的划分和设计必须依赖对通用语言、领域概念和模型的正确把握。接下来再让我们看两个我们经常遇到的例子:销售订单和论坛主题。

#两个例子:销售订单(Sales Order)/订单明细(Sales Line) vs. 论坛主题(Post)/回复(Reply)

很多网友会在这两个领域的建模上感到纠结,如果我们从数据库设计上考虑(以数据库驱动的开发方式进行思考),两者非常相似,都是主从表结构,都是1对多(1:N)的关系:一个销售订单对应多条订单明细,一个论坛主题对应多条回复。但如果我们用领域驱动的思想来考虑这个问题,我们会发现,这是两个截然不同的例子!两个例子中实体之间的关系完全不同。

首先分析销售订单(Sales Order)/订单明细(Sales Line):对于一张销售订单来说,订单明细是不可缺少的,否则就不成其为销售订单。试想,一张订单没有包含任何购买的货品信息,这意味着什么?因此,销售订单和订单明细之间的关系是一种固定的不可变(invariant)的关系,就像《领域驱动设计》一书中所讲的汽车与车轮之间的关系那样,汽车少了轮子就不成其为汽车了。反过来看,订单明细也离不开销售订单,这很简单,因为很明细订单明细是描述销售订单的一个不可或缺的部分。于是,在这个例子中,我们有一个聚合根为销售订单,其中包含一条或多条订单明细的聚合,聚合及其实体间的关系可以用下图表示:

对于论坛主题(Post)/回复(Reply)之间的关系,情况却完全不同。论坛的主题是可以脱离回复单独存在的(一个主题可以没有任何人对其进行回复),而回复却不能脱离主题(没有主题的回复是没有意义的)。鉴于这样的事实,实际上在主题与回复这部分模型中,存在两个聚合:第一个聚合是以主题(Post)为聚合根,且仅包含其本身一个对象的聚合;另一个聚合是以回复(Reply)为聚合根,其中包含了对主题(Post)的引用的聚合。其关系可以如下表示:

这样的设计,会让有些朋友感到不适应,原因是我们无法直接从Post实体获得其下所有的Reply实体,那么对于“通过给定的Post,获得与它相关的所有Reply信息”这样的用例,在实现上就不那么直接。此时,我们需要在应用层,通过Reply的仓储来获得,比如:

public IEnumerable<ReplyDataObject> GetRepliesForPost(Guid postId)
{
    using (IRepositoryContext context = IoCFactory.GetService<IRepositoryContext>();
    {
        ISpecification<Reply> spec = Specification<Reply>.Eval(r => r.Post.Id == postId);
        IRepository<Reply> replyRepository = context.GetRepository<Reply>();
        IEnumerable<Reply> replies = replyRepository.FindAll(spec);
        List<ReplyDataObject> result = new List<ReplyDataObject>();
        if (replies != null)
        {
                replies.ToList().ForEach(r => result.Add(DataObjectMapper.MapToDataObject(r));
        }
        return result;
    }
}

这部分内容牵涉到了应用层,或许你会觉得,这样做是不是把业务逻辑迁移到了应用层,导致领域模型失血。其实不然,在这里,应用层并没有参与任何业务逻辑,从仓储读取领域对象以及将领域对象转换成数据传输对象(DTO),这些并不属于业务逻辑的范畴:因为从领域模型和业务逻辑的角度看,它们并不能知道什么是仓储、什么是规约、什么是数据传输对象。应用层在这里起到了任务协调、数据转换等作用。不仅如此,应用层甚至还可以包含业务规则引擎以及工作流的实现(workflow)。

你如何进行领域建模

#运用四色建模法进行领域分析

领域建模有很多种方法,对于同样的问题域使用不同的建模手段得到的模型可能也不尽相同。于是我经常听到这样一个问题:怎么才能保证建模的正确性?

这听起来是个合理的质疑,但实际上却不是那么有道理。首先我们需要明白建模的目的是什么?如果仅仅是为了描画问题,那么并没有什么对错之分——仅仅是立场和角度的差别;而如果是为了企业业务系统而进行建模,那么这个问题应该变为:如何保证模型能够支撑企业的运营?

我想用下面这个例子来简要的回答一下这个问题。

在开始分析和建模之前,我们需要知道企业业务系统的目的是什么;而企业业务系统的目的往往跟决策者或者管理的诉求相关。我们现在需要移情到一位管理者身上,看看他的诉求到底是什么。

现在假想你是一家在线电子书店的 COO。突然有一天,有一位顾客向你投诉,说他订购的书少了一本,并且价钱算错了,他多给了钱。在你承诺理赔之前,你需要核对一下这位顾客说的是否属实。那么这个时候你需要知道什么样的信息才能做出准确的判断呢?

简单来说,你需要知道这位顾客订购了那些书籍,付了多少钱以及书店到底为这个顾客递送了那些书籍。不幸的是,由于科技不够发达,你无法直接驾驶时间机器回到从前去亲眼看看发生了那些事。但幸运的是,你并不需要这么做,你只需要看看这位顾客的订单,和网银的支付记录以及你们书店交给EMS的快递单存根,就应该知道这些信息了。

你找到了订单和 EMS 快递存根。发现这位顾客是在三天前订购的书,而你们在前天就已经将书邮寄出去了。并在订单上看到这位顾客一共订购了7本书,但是在EMS的快递存根上,并没有任何书籍的信息,只有地址,包裹号,邮费和重量什么的信息。这时候你觉得应该去询问一下配送部门,看看他们做了什么。

在配送部门你根据包裹号查到了那个包裹的信息,果然里面只有6本书。同时你在包裹部门发现了一张延期交货单。上面说明由于缺货,这位顾客另外一本书正在等待发货。

那么剩下的问题就是支付问题了,从网银的记录上看,客户不含邮费一共支付了132.5。订单上显示的价钱也是132.5,显然这位顾客并没有多付钱。

为了保证准确,你重新从网站上选了这7本书,想看看是否也会是这个价钱。但你却意外的发现,一共只需要128.3。仔细辨认后,你发现有一本图书现在是促销。那么现在的问题是,促销到底是什么时候开始的?

你到了市场部,市场部给了你一份近期促销计划。你发现那本书是昨天才开始促销的,也就是说在那位顾客在下订单的时候,促销还没有开始。

这个时候,你觉得应该给你的顾客打一个电话致歉,商讨如何后续邮寄的问题,并向他说明促销的事情。

你是否觉得这个 COO 当得有点累呢?这当然是虚构的。但是从这故事里面我们看到什么呢?

任何的业务事件都会以某种数据的形式留下足迹

我们对于事件的追溯可以通过对数据的追溯来完成。正如上面这个故事里,你无法回到从前去看看到底发生了什么,但是却可以在单据的基础上,一定程度的还原当时事情发生的场景。当我们把这些数据的足迹按照时间顺序排列起来,我们几乎可以清晰的推测出这个在过往的一段时间内到底发生了那些事情。

那么为什么这些数据形成的链条能够成帮助我们追溯业务的营运呢?

因为这些数据并不是随便挑选的。如果我们回顾一下你作为COO检查这个疏漏的过程,你首先选择了订单和EMS快递存根,换句话说,如果订单出现差错,或者EMS快递存根上说明你的确邮寄了7本书,那么这个疏漏的责任并不在你。所以这两个订单实际上这个你这个企业法律责任的起点和终点。

当你确定这个疏漏的责任在你之后,你选择审查一些流程执行的结果,比如包裹存根。从而验证一些主要的业务流程执行的结果是否正确。换句话讲,这些数据是支撑你运营体系的关键流程的执行结果

正是由于这些数据是流程执行的结果,它们才使我们可以在不了解流程细节的前提下,对某些突发事件进行追述和分析。

除了上面那个极端的例子(投诉),对于任何一笔正常的经济往来,我们都需要知道:

  1. 如果我付出一笔资金,那么我的权益是什么?
  2. 如果我收到一笔资金,那么我的义务是什么?

而这些问题都需要业务系统捕捉到相应的足迹才能够回答。所以企业的业务系统主要的目的之一,就是记录这些足迹,并将这些足迹形成一条有效的追溯链。

而作为业务分析师的你,则应该知道那些事件在运营上是需要追溯的,这些事件都留下了什么足迹。

这些足迹通常都具有一个有意思的特性,即它们都是时标性对象(moment-interval)。发现这些时标性对象就是建模的起点。对于这些时标性对象稍加整理,我们就得到了整个领域模型的骨干:

在得到骨干之后,我们需要丰富这个模型,使它可以更好的描述业务概念。这时候,我们需要补充一些实体对象。通常实体对象有三类:人,地点, 物(party/place/thing)。

在这个基础上,我们可以进一步抽象这些实体事如果参与到各种不同的流程中去的,这时候,我们就需要用到角色(role):

最后再把一些需要描述的信息放入描述对象(description)。

我们就得了应用四色建模方法(color modeling)建立的一套领域模型。

简要回顾一下上面的过程,不难发现我们建模的次序和重点:

  1. 首先以满足管理和运营的需要为前提,寻找需要追溯的事件。
  2. 根据这些需要追溯,寻找足迹以及相应的时标性对象。
  3. 寻找时标对象周围的人/事/物
  4. 从中抽象角色
  5. 把一些信息用描述对象补足。

由于在第一步中,我们就将管理和运营目标做为建模的出发点。因此,整套模型实际上是围绕这些“如何有效地追踪这些目标”而建立的,这样的模型可以保证模型支撑企业的运营。

#四色建模法

(Color UML)是由 Peter Coad 发明的一种建模方法,将抽象出来的对象分成四种原型(archetype):

  1. moment-interval:这种对象表示那些在某个时间点存在,或者会存在一段时间的,这样的对象往往表示了一次外界的请求,比如一次询价(Quotation),一次购买(Sale),这样的对象表示的都是系统的价值所在,所以也是最重要的一类对象,一般用粉红色来表示。这样的对象一般都有一个起始时间和终止时间,以及一个唯一的标识号,用来唯一的标识这一次客户请求,比如 PolicyNo.
  2. Role:这种对象表示的是一种角色,往往由人或者物来承担,会有相应的责任和权利,一般一个 moment-interval 对象会关联多个 Role,比如说一次询价(Quotation)涉及到两个 Role, 询价人(Quoter)和询价的产品(Product for Quotation), 这类对象是除 moment-interval 对象外最重要的一类对象,一般用黄色来表示。这类对象一般都有一些被 moment-interval 对象请求的操作,用来完成它们的职责。
  3. Party:Place or Thing, 这种对象往往表示的是一种客观存在的事物,例如:人,组织,产品,配件等等,这些事物往往会在一种 moment-interval 中扮演某个 Role, 比如某个人会在一次购买中扮演 Customer 的角色,也可以在询价中扮演询价人的角色。这类对象第三重要,所以一般用绿色来表示。这类对象一般都有 Name, Address 等属性。
  4. Description:这种对象一般是分类用或者描述性的对象,一般某个 Thing, Place,Party会属于某个 Description,主要用来表示一类事物,它的属性一般都是这一类事物都有的属性,这类对象一般用蓝色来表示。这类对象一般都有 type, defaultValue 等属性。

#总结

通过将分析得到的领域对象分别归入这四类原型,能让我们更加深刻的理解每个对象的职责,以及对象之间的相互关系,通过四种颜色,能表达出比一般的黑白模型更加丰富的领域信息。

四色建模法和别的建模方法相比,更倾向于作为一种分析方法,而不是设计方法,它也可以看作是一种分析模式,和 Martin Fowler 的《分析模式》有异曲同工之妙。在《Java Modeling in Color with UML》这本书中,Peter Coad 给出了多个行业的通用对象模型,包括制造业,资源管理,人力资源管理,财务管理等等,当然都是用四色建模法表示的,确实有耳目一新之感。

猜你喜欢

转载自www.cnblogs.com/lingboweifu/p/11897778.html