架构设计内容分享(九十三):业务分析-面向服务分析

目录

1向服务分析与设计

2领域驱动设计

3领域模型设计的推荐实践


1向服务分析与设计

面向服务(SOA)是一种应用框架,它将应用程序的不同功能单元(即服务),通过各单元之间定义良好的接口和契约联系起来。其中接口是采用中立的方式进行定义的,它独立于实现服务的硬件平台、操作系统和编程语言,这得构建在各种系统中的服务可以以一种通用的统一方式进行交互。它的实现过程就是面向服务的分析与设计(Service-Oriented Analysis and Design,SOAD)。

对于传统的面向服务(SOA)框架的实现方式主要有两种:Web Service和ESB,其实现方式和原理如下。

1-1Web Service模式

在Web Service模式中,首先,服务提供者创建服务,并决定将其公开并发布。发布是通过将服务信息发布到服务目录上来完成的。另一方面,服务消费者将在服务目录中搜索满足必要条件的服务,找到服务后,通过服务目录中的可用服务信息,服务消费者就能够以正确的方式直接调用服务提供者,从而满足业务需求。其过程如下图所示

图片

// 关于Web Service中一些术语的解释说明如下

  • 服务提供者:发布了调用契约和位置的服务提供者; 

  • 服务消费者:服务目录中找到的与其业务需求匹配的服务的使用者;

  • 服务目录:用于发布服务和消费者列出可用服务的目录;

  • SOAP协议(Simple Object Access Protocol,简单对象访问协议):是交换数据的一种协议规范,是一种轻量的、简单的、基于XML的协议,它被设计成在WEB上交换结构化的和固定化的信息。

  • WSDL(Web Services Description Language,Web服务描述语言):用来描述Web服务发布的XML格式;

  • UDDI(Universal Description Discovery and Integration):是一种用于描述、发现、集成Web Service的技术,它是Web Service协议栈的一个重要部分。通过UDDI可以根据自己的需要动态查找并使用Web服务,也可以将自己的Web服务动态地发布到UDDI注册中心,供其他用户使用

1-2ESB模式

ESB(全称:Enterprise Service Bus,即企业服务总线)以广为接受的开放标准为基础来支持应用之间在消息、事件和服务级别上动态的互连互通,是一种在松散耦合的服务和应用之间标准的集成方式。它提供了网络中最基本的连接中枢,消除不同应用之间的技术差异,让不同的应用服务器协调运作,实现了不同服务之间的通信与整合。其架构如图所示。

图片

// ESB的核心内容

  • 服务元数据管理:包括服务注册、生命周期等,一般使用注册仓库(Registry Repository)方式实现。

  • 协议适配:支持各种集成和通信协议,支持各种消息传输和业务集成方式。

  • 中介服务:支持各种集成场景,支持各种消息处理与转换模式,特别是对EIP的支持。

  • 治理与监控:服务调用与消息处理的日志及统计分析,服务质量、服务降级,流控等等。

  • 安全性:传输通信安全性,数据安全性、服务调用安全性,身份验证等等。

  • 其他还有事务管理、高性能、高可用、高可靠性、高稳定性等等。

在企业内部IT生态环境中,各个系统上的业务服务构成了企业IT的核心资产。使用ESB来整合和管理分布在各个业务系统中的服务,从而使得ESB变成企业IT内部的核心基础部件。

1-3微服务和SOA

微服务是SOA 架构的延续,它是SOA发展出来的产物,是一种比较现代化的细粒度的SOA实现方式,它强调业务需要彻底的组件化和服务化,也就是系统需要按照业务边界做细粒度的拆分和部署。这样原有的单个业务系统会拆分为多个可以独立开发、设计、运行的微服务系统,这些微服务之间通过REST(Representational State Transfer,简称REST)方式完成交互和集成。 

那么微服务又具有什么样的特点呢?简单归纳如下3点:

01独立部署、灵活扩展

传统的单体架构是以整个系统为单位进行部署,而微服务则是以每一个独立组件为单位进行部署。它可以根据每个服务高并发要求的不同而进行不同的部署,比如,A服务需要部署10台机器,B服务则需要部署20台机器,而C服务只需要部署3台机器。像这种灵活的部署只有微服务架构才能实现,而近几年流行的容器化,更为微服务架构提供了高效灵活的部署方案。

02资源的有效隔离

微服务设计的原则之一,就是每一个微服务拥有独立的数据源,假如微服务A想要读写微服务B的数据库,只能调用微服务B对外暴露的接口来完成。这样有效避免了服务之间争用数据库和缓存资源所带来的问题。另外,由于每一个微服务实例都运行在不同的容器中,这也更好地实现了服务器资源(比如内存、CPU等)的隔离。如图所示

图片

03敏捷的组织结构

微服务设计的思想也改变了原有的企业研发团队的组织架构,从以往水平的职能型团队变成了垂直的业务型团队。职能型团队和业务型团队的架构,如图所示

图片

当然在微服务带来好处的同时也随之产生了各种各样的问题,以下为微服务中常见的问题:

微服务把原有的项目拆成了多个独立的工程,从而增加了开发、测试和运维的复杂度。微服务架构需要保证不同服务之间的数据一致性,引入了分布式事物和最终一致性机制,为设计和开发带来了挑战。很多微服务拆分粒度过大,导致服务的调用链过长,性能降低了,维护成本变高了。

当微服务遇到领域驱动设计(DDD),可以弥补微服务中很多的不足,为微服务提供指导思想,比如像服务拆分的粒度和事物合理设计等,下面将重点展开介绍领域驱动设计的详细方法。

2领域驱动设计

领域驱动设计(Domain-Driven Design DDD,DDD)是一种将实现连接到持续进化的模型中来满足复杂需求的软件开发方法,旨在通过将软件的相关部分连接到不断发展的模型中来简化复杂应用程序的创建。领域驱动设计侧重于以下三个核心原则。

  1. 把项目的主要重点放在核心域(CORE DOMAIN)和域逻辑。

  2. 把复杂的设计放在有界域(BOUNDED CONTEXT)的模型上。

  3. 和领域专家不断协作完善应用模型来解决特定领域的问题。

所以,在领域驱动设计中非常重要的一个概念就是:领域。而领域又可以划分为问题域和问题解决域。领域中各概念之间的关系如图所示。

图片

领域是一个组织所做的事情及其中所包含的一切。企业通常会确定一个市场,然后在这个市场上销售产品和服务。每个组织都有它自己的业务范围和做事方式,这个业务范围及在其中进行的活动便是领域。

领域驱动设计就是先将在领域中涉及的数据、流程、商业规则等都弄明白,然后以面向对象的观点为其建立一个领域模型,再选用合适的软件技术去实现这个模型。在领域中涉及的数据、流程、商业规则等就是我们常说的领域问题(问题域),对于大的问题又会按功能或重要程度划分为不同的子问题(子域),采用分而治之的思想;领域模型及使用合适的软件技术去实现个模型就是我们常说的问题解决方案(问题解决域)。下图展示了领域和各子域的关系。

图片

限界上下文映射

在进行限界上下文划分之后,我们还需要进一步梳理限界上下文之间的关系。通过限界上下文映射关系,能够明确限界上下文之间的耦合关系。这里主要关注限界上下文内部交互和限界上下文外部交互的耦合性,以最大限度地减少耦合度的层级。限界上下文之间的映射关系有以下几种。

  • 合作关系(Partnership):两个上下文紧密合作的关系,一荣俱荣,一损俱损。

  • 共享内核(Shared Kernel):两个上下文依赖部分共享的模型。

  • 客户方-供应方开发(Customer-Supplier Development):上下文之间有组织的上下游依赖。

  • 遵奉者(Conformist):下游上下文只能盲目依赖上游上下文。

  • 防腐层(Anticorruption Layer):一个上下文通过一些适配和转换与另一个上下文交互。

  • 开放主机服务(Open Host Service):定义一种协议来让其他上下文对本上下文进行访问。

  • 发布语言(Published Language):通常与OHS一起使用,用于定义开放主机的协议。

  • 大泥球(Big Ball of Mud):混杂在一起的上下文关系,边界不清晰。

  • 无关系(Separate Way):两个完全没有任何联系的上下文。

耦合度就是某模块与其他模块之间关联、感知和依赖的程度,是衡量模块独立性的一个指标,也是系统分析与设计中的重要指标。

在理解领域和领域中各概念的关系后,接下来将详细介绍领域驱动设计中的核心元素,如下所述。

  • 分层架构(Layered Architecture):分层架构的一个重要原则是每层只能与位于其下方的层发生耦合。分层架构可以简单分为两种,即严格分层架构和松散分层架构。在严格分层架构中,某层只能与位于其直接下方的层发生耦合;在松散分层架构中,允许某层与它的任意下方层发生耦合。DDD分层架构图如下。

图片

  • 应用层(Application):负责展现层与领域层之间的协调关系,协调业务对象来执行特定的应用程序任务。它不包含业务逻辑,所以相对来说是较“薄”的一层。

  • 领域层(Domain):负责表达业务的概念,实现全部的业务逻辑并且通过各种校验手段保证业务的正确性,是核心部分。那什么是业务逻辑呢?它包括业务的流程、策略、规则、状态及完整性约束等,所以领域层是较“胖”的一层。

  • 实体(Entity):实体是一个不由自身属性定义而由它自身身份定义的对象,具有状态和行为。实体对象具有唯一性并且是可持续变化的,也就是说在实体的生命周期内,无论如何变化,其仍旧是同一个实体。它的唯一性由唯一的身份标识来决定的,而它的可变性也正反映了实体本身的状态和行为。

  • 值对象(Entity Object):只包含元素属性的不可变对象。值对象是将一个值用对象的方式来表述,进而表达一个具体的固定不变的概念。比如某个地址(Address)对象,它不用唯一身份标识id来决定它的唯一性,只通过固定不变的概念来表示一个具体的地址就好。

  • 服务(Service):服务分为领域服务和应用服务,强调与其他对象的关系,只定义了可以为客户做什么,而不应该替代实体(Entity)和值对象的所有行为,也就是常说的充血模型。

  • 应用服务:是用来表达用户故事(User Story)和用例(Use Case)的主要手段。应用层通过应用服务接口来暴露系统的全部功能。在应用服务的实现中,它负责编排和转发,将要实现的功能委托给一个或多个领域对象来实现,它本身只负责处理业务用例的执行顺序及结果的拼装。通过这样的方式能很好地隐藏领域层的复杂性及其内部的实现机制。

  • 应用层:在该层除了可以定义应用服务,还可以进行安全认证、权限校验、持久化事务控制、调用外部系统或者向其他系统发送事件消息等。另外,应用层是展示层与领域层的桥梁,展示层使用VO(视图模型)进行界面展示,与应用层通过DTO(数据传输对象)进行数据交互,从而达到展示层与DO(领域对象)解耦的目的。

  • 领域服务,当领域中的某个操作过程或转换过程不是实体或值对象的职责时(比如跨多个领域对象的操作),我们便应该将该操作放在一个单独的接口中,即领域服务。领域服务是用来协调领域对象完成某个操作,用来处理业务逻辑的,它本身是一个行为,所以是无状态的,状态由领域对象(具有状态和行为)保存。

  • 模块(Module):是指提供特定功能的相对独立的单元,也就是对功能的分解和组合。模块的用途是通过分解领域模型为不同的模块,来降低领域模型的复杂性,提高领域模型的可读性。

  • 聚合(Aggregate):聚合是由聚合根(ROOT ENTITY)绑定在一起的对象的集合,是领域对象的显示分组,用来表达整体的概念(也可以是单一的领域对象)。它的宗旨是支持领域模型的行为和不变性,同时充当一致性和事务性边界。聚合根通过禁止外部对象对其成员的引用来保证在聚合内进行的更改是一致的,所以它的难点一般在于对一致性的维护上:聚合内实现事务一致性,聚合外实现最终一致性。

在进行聚合设计时要遵循怎样的原则呢?主要有如下几点:

  1. 遵循领域不变性。

  2. 聚合内实现事务一致性,聚合外实现最终一致性。每个事物一次仅更新一个聚合。当业务用例要跨域多个聚合时,使用领域事件进行事务拆分,实现最终一致性。

  3. 使用小聚合,避免把聚合当作领域对象的集合或容器,从而出现“巨大”的聚合根。

  4. 不仅仅是HAS-A关系,这点与面向对象(OO)思想中的聚合不是一个概念。DDD中的聚合不是简单的包含关系,要确定包含的领域对象是否为了满足某个行为或不变性。

  5. 通过聚合根完成聚合中领域对象的持久化和检索。

  6. 优先使用值对象,聚合根内的其他领域对象优先设计成值对象。

  7. 使用ID关联,而非对象引用。对象引用不仅会导致聚合边界的模糊,而且会导致延迟加载的问题。

  8. 聚合边界之外的对象不能持有聚合内部对象的引用;聚合内部的领域对象可以持有其他聚合根的引用。

  9. 避免在聚合内使用依赖注入。对于依赖的对象,我们应该在调用聚合方法之前查找获取并通过参数传递。比如,可以在应用服务中通过依赖注入资源库或领域服务获取聚合依赖的对象,然后传入聚合。

  • 工厂(Factory):工厂是用来封装对象创建所必需的知识,它们对创建聚合特别有用。一个对象的创建可能是它自身的主要操作,但是复杂的组装操作不应该成为被创建对象的职责,因为组装这样的职责会产生笨拙的设计,也很难让人理解。而工厂可以帮助封装复杂对象的创建过程,并且当聚合根建立时,所有聚合包含的对象也随之建立了,整个过程是又是原子化的。

  • 仓储(Repository):仓储是对聚合的管理,它介于领域模型和数据模型之间,主要用于聚合的持久化和检索,同时对领域模型和数据模型进行了隔离,以便我们关注于领域模型而不需要考虑如何进行持久化。

仓储对于初学者来说比较迷惑的,感觉是多余的,为什么不能直接使用数据访问层(比如ORM框架等),而需要多加一个仓储层来解耦呢?下面主要介绍下仓储与数据访问层的区别:

  1. 仓储限定了只能通过聚合根来持久化和检索领域对象,以确保所有改动和不变性由聚合根处理,保证聚合内的一致性。

  2. 仓储通过隐藏聚合持久化和检索的底层技术实现,从而达到了与具体实现的解耦能力,即领域层不需要知道通过什么方式来实现持久化领域对象的。比如当前使用的是Hibernate,而未来想改为Mybatis,而这样的变动对于领域层来说是完全透明的。

  3. 仓储为数据模型和领域模型定义了一个边界,解决了领域模型和数据模型的混乱情况。(很多人错误地把ER数据模型当作了领域模型)

虽然各元素的责任不一样,所负责的层级也不同,但是各元素之间并非独立的,其关系如下图所示:

图片

当撑握了如何做好系统分析和设计的理论思想后,如何才能更好地在工作中令实践落地呢?下面推荐一些个人的经验和总结,我把它整理为领域模型设计的最佳实践方法。

3领域模型设计的推荐实践

3-1什么是领域模型

领域模型(domain model)是对领域内的概念类或现实世界中对象的可视化表示。领域模型也成为概念模型、领域对象模型和分析对象模型。领域模型是一种概念模型,也叫问题域模型。它表述的是某个领域的现实概念。

经常有人把物理数据模型(比如数据库的ER模型)和领域模型混为一谈,其实这是一个误区。物理数据模型在本质上归属于结果域模型(Solution Space Model),是对某个问题域的解决方案的一个具体描述。而领域模型是为了准确定义需要解决的特定问题而构造的抽象模型,其最重要的功能是形成统一的认知,也就是说所有相关人员都需要对要解决的问题有一个完整、规范和一致的认知。

我们经常在公司听到一句话:一颗心,一张图,一场仗。其意思是说我们首先要明白解决什么问题(一场仗),然后才能把这个问题毫无歧义地表述成统一的模型(一张图),有了这模型,我们就能全心全意地投入到解决这个问题的过程中,这样就能起到对问题的统一认知(一颗心)。所以,如果大家在工作中经常争论一个问题,则很可能是因为大家对问题的定义还没有统一的认知。而系统、科学、有逻辑地定义我们的问题,从而达到对要解决的问题的统一认知,就是领域建模的过程。

领域建模通过对领域内的概念或现实世界中的对象进行可视化表示,领域建模专注于分析问题域本身,发掘重要的业务领域概念,并建立业务领域概念之间的关系,所以它是对业务角色和业务实体之间应该如何联系和协作以完成业务功能的一种抽象。

3-2领域模型的作用

领域模型是业务概念的可视化描述,是需求分析的产物,领域模型用于指导程序设计,但领域模型与实现方式无关,领域建模时不应该考虑如何实现,领域模型需要同项目所有成员(客户、项目经理、开发、测试…)达成共识,为什么要做领域建模

  首先,建模的重要性在所有工程实践中都已经得到了广泛的认同。建模是一种抽象和分解的方法,它可以将复杂的问题拆解成一个个抽象,代表了特定的一块密集而内聚的信息。

  上世纪80年代开始,人们对于面向对象建模产生了许多思考和方法,其中最流行的就是面向对象分析与设计。面向对象分析,强调的是在问题域发现并描述概念,解决的问题是做正确的事情。面向对象设计,强调的是定义软件对象,解决的问题是正确的做事情。

  领域模型就是面向对象分析的主要产物,它表达了对现实问题的描述和抽象。

图片

如果不做设计直接实现,俗称走一步看一步。很大可能在开发过程中发现思维局限,开发进度推倒重来。

如果不做分析直接设计,看起来没什么问题。遗憾的是,通过这种方式构造的代码,并没有和现实世界连接起来,当我们的软件和需求稍加修改,这份代码就可能变得异常混乱和难以维护。而通过领域建模的,自上而下的设计,可以保证代码实现的层次结构和模块划分是科学的、稳定的。

同时随着时间的迁移,系统的演变导致系统之间的依赖越来越复杂。原因:模型设计与现实世界走向不一致,导致模块职责越来越不明确,系统之间的交互越来越复杂,最终整个系统变得难以维护。

举一个例子,在某电商系统的初期设计中,一个卖家账号只能开一个店铺,因此卖家和店铺的概念全部由卖家这一个模型来承载。所有店铺相关的模型(店铺红包、店铺评价等)全部与卖家模型做关联。这时同一个模型拥有了两层业务含义,职责不明确。

领域建模可以降低软件和现实世界之间的差异,用真实的业务概念划分职责,目的是实现一个可以高效低成本维护的可持续发展的软件系统。

从领域模型推导到系统实现是一套引导思考的方式,也是一套科学的开发流程。其核心目的在于提供了系统设计的“指导方针”。领域模型必须站在用户需求和业务发展的角度上,既可以用来同客户沟通验证需求,又可以避免模型因实现的考量而带偏(实现成本、遗留系统)。

3-3如何进行领域建模

领域建模的方法也有很多种。下面列出的是一些常见的方法。需要注意,领域建模是需要依赖大量经验和思考的,各种方法只是引导的作用。

01用例分析法

用例分析法是进行领域建模最简单可行的方式。其步骤如下:

  1. 获取用例描述:既然我们的领域模型指的是问题域模型,那么建模也一定要从问题域入手。那么问题域的知识如何表现出来呢,一个最常见的方式就是通过用例,也可以通过场景来分析——总之就是一段格式化的需求文字描述。

  2. 寻找概念类:寻找概念类就是对获取的用例描述进行语言分析,识别名词和名词短语,将其作为候选的概念类。当然,需求描述中的名词不可能完全等价于概念类,自然语言中的同义词、多义词都需要在此处进行区分。还有很多名词可能只是概念类的属性,不过没关系,在这一步骤可以都提取出来,在第四步再区分出概念类和属性。

  3. 添加关联:关联意味着两个模型之间存在语义联系,在用例中的表现通常为两个名词被动词连接起来;并非所有动词关联的概念类都需要作为关联存在,更重要的是我们需要判断,两个概念类的关系是否需要被记住;试想你是一个业务员,如果某两个概念类的实例之间的关系没有任何人知道,是否会阻碍业务的开展。如果答案是肯定的,那么说明这两个概念类存在关联。如果答案是否定的,那么建议不要加上关联(视情况,也要考虑逻辑上二者的关系是否“被记住”)。应该尽量避免加入大量关联,关联不代表数据流,也不代表系统调用关系。

  4. 添加属性:对于上文抽取到的名词列表,我们需要区分概念类和属性(当然名词列表也会有无用的词语)。如何判断一个名词是否是属性?能完全通过基本数据类型(数字、文本、日期)表达的大多是属性。如果一个名词只关联一个概念类,并且它自身没有属性,那么他就是另一个概念类的属性。注意这里表达的依然是业务概念,外键ID不是属性

  5. 模型精化:可选的步骤,有时我们希望在领域模型中表达更多的信息,这时会利用一些新的手段来表达领域模型:包括泛化、组合、子域划分等领域模型可以使用UML的泛化和组合表达模型间的关系,他们表达的是概念类的”is-a”和”has-a”的关系,并不是实现的软件类的关系。然而,也一些方法论中并不建议使用这种方式来表达领域模型,因为这种精化的领域模型不利于和需求方沟通。子领域划分是常见的拆解领域的方式,通常来说我们会将更内聚的一组模型划分为一个子领域,形成更高一层的抽象,利于系统的表达和分工。

02DDD的方法

DDD本身是一套完整、详尽的方法论,从如何需求沟通(构建领域知识),到高层设计(战略建模)、详细设计(战术建模),细致到代码的实现风格都给出了示例。本文无意也无力来详述DDD的所有知识,但是关于如何建模,DDD给出了很多思想可以借鉴。

  需要再次强调的是,DDD的模型本质上是solution space的模型,然而DDD强调模型与实现绑定,因此这里指的“模型”,可以说是领域模型,也可以是系统模型。下面就是DDD建模的一般步骤:

1.构建领域知识

软件的最终目的是增进一个特定的领域。为了达到这个目的,软件需要跟要它服务的领域”和谐相处”。所谓和谐相处,软件需要精确地反应领域概念和知识,以更好的适应变化。

因此,软件开发者第一步也是最重要的一步就是理解领域知识。DDD鼓励开发者和领域专家工作在一起,通过交谈和提问,让开发者学习到领域知识,挖掘出领域的关键概念。

2.创建通用语言

通用语言是领域专家和开发团队之间定义的标准的术语。目的是把领域知识更完善地传达到软件中。

3.团队在进行所有方式的沟通时(文字,演讲,图形)都需要采用这种一致的语言。

通用语言需要映射到模型中,映射到代码里。做到通用语言的更改就是对模型的更改,也是对代码的更改。

4.创建实体

基于通用语言和领域知识,需要首先分辨出实体。

实体是领域中需要唯一标识的领域概念。如果两个实体所有状态都一样,但标识不一样,就是两个不同的实体。

实体同样需要属性来描述

5.创建值对象

值对象是领域中不需要唯一标识的领域概念。

如果两份对象所有状态都一样,我们就认为是同一个值对象。值对象也可以理解为一组聚合的属性。例如地址信息,类目信息。

6.创建聚合根

聚合根是一个实体,将一组模型聚合在一起,与外部模型划分开来。这一组模型全部关联着聚合根,只有聚合根负责与外部访问。

聚合根有助于保持领域模型关联的简化和生命周期的维护。

03四色建模法

四色建模法源于《Java Modeling In Color With UML》,它是一种模型的分析和设计方法,通过把所有模型分为四种类型,帮助模型做到清晰、可追溯。

图片

  • moment-interval(时标性原型):时标性原型是建模的起点,它代表着我们需要记录的,某一时刻发生的事件。例如订单,行程,会议。

  • party, place, or thing(人-事-物原型):一种有形的,可唯一识别的实体。可以是人、机构、地点、物品等。

  • role (角色原型):角色是party, place, or thing的一种参与方式。例如,在一份雇佣关系中,某个人扮演者雇员的角色。那么这个人就是”party, place, or thing”,雇员就是”role”。

  • description(描述原型):表示资料类型的资源,是一种类似目录条目的描述,用来对对象进行分类或标记,可以被其它原型反复使用。例如,一个商品的品牌、描述属性。

  • 建模次序

  • 首先以满足管理和运营的需要为前提,寻找需要追溯的事件。

  • 根据这些需要追溯,寻找足迹以及相应的时标性原型。

  • 寻找时标对象周围的人-事-物。

  • 从中抽象角色。

  • 把一些信息用描述对象补足。

提到模型,大多数人的第一反应就是一个类或者对象,这里指的系统模型就是这种概念。

为了保证程序的实现遵循领域模型的思想,并让所有人都对领域和职责的认知没有偏差,这里强烈建议每个领域模型都要有一个系统模型与之对应,最好能完全一一对应(领域驱动设计就是这么做的),它们的命名和属性也尽可能保持一致,使用相同的术语。具体到系统模型的设计,就是面向对象设计的范畴了,可以使用各种各样的设计模式、GRASP、SOLID去设计和规划,这里不再赘述。需要遵循的宗旨是,领域模型的模型职责、子域边界划分应该作为此处设计的指导原则,每个模块都不可以突破这些职责的约束。

  • 19
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

之乎者也·

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值