首页 文章 精选 留言 我的

精选列表

搜索[架构师],共6917篇文章
优秀的个人博客,低调大师

系统架构师-基础到企业应用架构-分层[上篇]

一、前言 大家好,接近一年的时间没有怎么书写博客了,一方面是工作上比较忙,同时生活上也步入正轨,事情比较繁多,目前总算是趋于稳定,可以有时间来完善以前没有写完的系列,也算是对自己这段时间工作和生活上总结,同时也加深下自己对架构和 设计方面的理解,由于本人的写作水平有限,所以在书写的深度和书写的格式上还有很多的缺点,还希望大家多多指出。 二、开篇 本篇我们将针对系统架构中的分层进行讲述,分析不同分层模式的优缺点及应用的场景,当然我们会结合一些案例来介绍这些分层,通过案例来证明各种分层的好处与优缺点,本篇作为开篇主要是介绍这个分层系列中会讲述到的几种分层模式实践, 由于很多分层模式也是自己在工作过程中总结和经验积累下来的,可能存在个人理解或用法上错误之处,还请大家指出,我予以及时更正。 三、内容提要 1、前言 2、开篇 3、本文提纲 4、分层模式 4.1、分层架构介绍 4.1、后端分层多层 4.1.1、普通三层架构 4.1.2、多层架构 4.2、前端分层模式 4.2.1、MVC模式 4.2.2、MVP模式 4.2.3、MVVM模式 5、结束语 6、系列进度 7、下篇预告 四、分层模式 4.1、分层架构介绍 架构首先是分为不同层次的和不同视图的,例如架构有五种视图:逻辑视图、物理视图、数据视图、运行视图、开发视图。我们今天不讲解这几个不同的视图,而是讲解分层对于软件设计的意义及关注点,之前我也发过一片单机软件架构的文章,文 章中提到了一个软件从简单到复杂的全过程,而软件架构也是一个迭代的过程,是一个循序渐进,不断完善的过程。 我们今天交流的主要是逻辑纬度的分层,关于物理视图的分层,本篇先不讲解,因为那块更复杂,同时也更重要,对于大型的互联网软件或大型的互联网网站,更关注的是物理架构方面的设计。下面我们就来针对当前的一些分层模式来进行讲解,并 且进行简要的分析和应用场景介绍。 4.2、后端分层架构 一、普通三层架构 三层架构(3-tier architecture) 通常意义上的三层架构就是将整个业务应用划分为:表现层(UI)、业务逻辑层(BLL)、数据访问层(DAL)。区分层次的目的即为了“高内聚,低耦合”的思想。 image 三层架构图 对于传统的三层架构图,可能因为大家在实际的场景中,因为大家对这些分层运用的不同,会出现适应的场景的不同,而且有很多的大型软件或项目,都是采用三层架构,我们可以通过引入一些开源的组件或自定义组件来构建非常灵活或扩展性很强 的分层结构,虽然是3层架构,但是却可以满足大部分的场景。 A、场景: 最原始的三层结构可能如下: image ThreeArchitecture.Entities:实体定义层,该层主要是完成各分层间数据传递并且最终通过该实体实现DAL层与数据库交互的数据传输。 ThreeArchitecture.DAL:数据访问层,通过调用实体层,通过Ado.net编程,实现数据持久化,例如可以支持多种数据库,sqlserver、oracle、mysql、sqlite. ThreeArchitecture.BLL:业务逻辑层,通过调用实体层、数据访问层,实现整个业务系统的核心功能,完成系统业务的处理。 ThreeArchitecture.UI:用户界面交互层,用户通过该用户界面与业务系统进行交互,完成业务逻辑操作与交互。 根据上面的解决方案的分层及组织,下面针对以下几个场景来分析,分析三层架构中遇到的问题,应该如何解决这些问题。 1)、如果需要实现多数据库支持。我想业务系统能够从sqlserver向oracle数据迁移,或反之。 这样在现有的项目结构方式,就无法满足,但是我们可以增加新的接口层来实现这个要求。 例如可以通过如下项目方式来组织: image 修改原有的项目划分结构,加入DAL.Interface层次。定义数据访问接口,通过不同的数据访问实现,然后通过数据访问层工厂,来构建不同的数据库访问实例。 这块具体的代码我就不贴出了,应该比较简单。 同时原来的ThreeArchitecture.BLL 调用的不是直接调用数据库访问层实现,而是调用数据访问层接口。不依赖于具体的实现,而是依赖接口,这样可以实现解耦,提供了很强的扩展性。 2)、如果我要求业务逻辑层实现也不一定固定,例如在医疗行业的话,每个医院的业务系统或业务流程都不相同,那么假设我们希望沟通统一的UI界面,而不是随着业务逻辑的改变而修改UI,那么我们就需要进行如下的设计: 项目的结构方式类似上面的DAL层的变化。 image 在原来的基础上改进: ThreeArchitecture.BLL.Interface:定义业务逻辑接口,主要目标是隔离UI与业务逻辑实现间的依赖关系,将实现代码调用修改为接口调用方式。 ThreeArchitecture.BLL.A:A场景下的实现,A的业务逻辑。 ThreeArchitecture.BLL.B:B场景下的实现,B的业务逻辑。 3)、纵向和横向扩展性需求场景,例如场景变化灵活性较高时,工厂模式无法很好应对,需要维护大量的工厂代码。 可以采用开源的相关组件,来实现解耦及隔离,例如 数据访问层可以采用Nhibernate或Entityframework来实现,关于Nhibernate的文章,园子里面已经有很多的文章介绍了,我就不介绍了, 引入Nhibernate以后,项目的结构,回到如下模式 image ThreeArchitecture.DAL.Nhibernate:NHibernate实现数据访问层接口,Nhibernate支持目录主流的大部分数据库,所以不需要按照1)中的方案去做,只需要实现一次即可。 ThreeArchitecture.DAL.EntityFramework:EntityFramework实现数据访问层接口,EntityFramework支持Oracle,SQLServer,其他的数据库支持的不太好。 在上面的场景中,例如在A场景下,我希望使用A业务层、B场景下使用B实现,而且,不希望系统中维护大量的工厂代码,那么我们就请出来当前架构或框架设计的核心组件IOC IOC:控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是当前主流框架的核心。 控制反转还有一个名字叫做依赖注入(Dependency Injection)。简称DI。 采用了IOC以后,接口和实现就可以通过配置的方式来动态的设置,而且调用的方式也变得更简单,不需要其他复杂的代码设定,目前市面上的IOC容器很多,我了解的主要是以下几种: Unity:微软的轻量级IOC容器。提供了比较强的注册和动态查找机制,同时提供了强大的AOP,几乎无所不在。 Autofac:Autofac是一款IOC框架,比较于其他的IOC框架,如Spring.NET,Unity,Castle等等所包含的,它很轻量级性能上也是很高的 Spring.NET:参考java的sprint 框架的.net平台下的实现,比较强大。 Castle:Castle是针对.NET平台下的一个非常优秀的开源项目,从数据访问框架 ORM到依赖注入容器,再到WEB层的MVC框架、AOP,基本包括了整个开发过程中的所有东西,为我们快速的构建企业级的应用程序提供了很好的服务 Ninject:是一个快如闪电、超轻量级的基于.Net平台的依赖注入框架。它能够帮助你把应用程序分离成一个个松耦合、高内聚的模块,然后用一种灵活的方式组装起来。通过使用Ninject配套你的软件架构,那么代码将会变得更加容易编写、重用性强、 易于测试和修改。 关于上面介绍的部分IOC容器的用法整体上来说都差不多,具体的大家可以网上搜索下,案例和demo比较多。 二、多层架构 上面介绍了普通的三层架构,多层架构顾名思义就是在三层架构之上,通过扩展及应用场景的挖掘,衍生出来的适应不同场景的架构模式,下面我主要是来介绍以下几种多层架构模式 A、服务层模式 在上面介绍的3层架构模式中,存在一个缺陷,如果我们构建的软件或系统支持分布式或者需要对外提供服务的时候,这个场景就无法满足了,所以这个时候服务层就出现了,就是在BLL层的基础上进行包装,包装成可以对外提供调用的分布式服务。 经过改造后的项目结构如下: image 在项目中加入了03.解决方案文件夹,同时添加项目 ThreeArchitecture.Service项目。 ThreeArchitecture.Service:主要是提供几个作用:1、将业务逻辑层进行封装,对外提供业务服务调用。2、通过外观模式,屏蔽业务逻辑内部方法。3、降低业务逻辑层与UI层的依赖,业务逻辑接口或实现的变化不会影像UI层。4、降低UI层调用的请求次 数及数据往返。 在上面的结构中,我们说了Service层次的作用,目前还少加入了一层,DTO(数据传输对象层),该层负责屏蔽后端的实体层,将UI层需要的数据进行重新的定义和封装,在实际的业务场景下,后端实现或存储的数据远比用户需要的数据要庞大和负责,所 以前端需要的数据相对来说要么是组合的,要么是抽取的,不是完整的,因为我们在设计数据存储格式上都会有一些额外的设计和考虑。 image 加入了ThreeArchitecture.DTO层后,前端的UI层,只是知道DTO的存在,同时前端需要的数据都在一个Dto中,这样,每次调用服务层的时候,只需要调用一次就可以完成所有的业务逻辑操作,而不是原来的直接调用业务逻辑层那样的,需要调用多 次,对于分布式场景下,减少服务调用的次数,尤其重要。 B、DDD架构模式: image Presentation Layer: 负责与客户端进行交互 Application Layer: 负责协调领域层之间的交互 Domain Layer: 软件的核心,所有相关的Domain information都在这,可以看成是Business Logic Layer,但不完全是 Infrastructure Layer: 負責各層之間的交互溝通、資料存取、安全性管理及通用Library 更常见的是如下层次 我们建议的方式如下: Repository层使用ORM映射或SQL命令等方式把持久化数据转化为领域对象,然后根据业务逻辑设计对应领域层服务Domain Service 。接着应用层进行操作上的协调,利用Repository、领域模型、领域层服务Domain Service 完成业务需要,再通过数 据转换器把领域对象Domain Object转化为数据传输对象DTO。最后,利用远程通讯技术把应用层的服务(Application Service)对外开放。 注意留意的是SOA系统中,UI表现层与Application Service应用层服务是实现分离的,表现层可以同时调用多方的远程服务来完成工作。 在上面的架构中还可以加入领域事件、查询接口、分布式服务层,来灵活运用和组合,来解决项目中适应场景的不同。 4.3、前端分层架构 A、MVC架构模式 MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑和数据显示分离的方法组织代码,将业务逻辑被聚集到一个部件里面,在界面和用户围绕数据的交互能被改进和个性化 定制的同时而不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。 目前在主流的框架中都支持该模式,例如构建winform程序中可以通过MVC模式来分离界面层中的控件与后端服务间的交互。降低耦合及依赖。 web上通过asp.net MVC框架来实现前端页面及后端控制器之间的隔离。 视图 视图是用户看到并与之交互的界面。对老式的Web应用程序来说,视图就是由HTML元素组成的界面,在新式的Web应用程序中,HTML依旧在视图中扮演着重要的角色,但一些新的技术已层出不穷,它们包括Adobe Flash和像XHTML,XML/XSL,WML 等一些标识语言和Web services. MVC好处是它能为应用程序处理很多不同的视图。在视图中其实没有真正的处理发生,不管这些数据是联机存储的还是一个雇员列表,作为视图来讲,它只是作为一种输出数据并允许用户操纵的方式。 模型 模型表示企业数据和业务规则。在MVC的三个部件中,模型拥有最多的处理任务。一个模型能为多个视图提供数据,由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。 控制器 控制器接受用户的输入并调用模型和视图去完成用户的需求,所以当单击Web页面中的超链接和发送HTML表单时,控制器本身不输出任何东西和做任何处理。它只是接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示返回的数 据。 ASP.NET MVC image 关于具体的代码,大家可以尝试新建一个MVC的应用程序,微软提供的默认的MVC的代码模版中就有相关的示例代码,具体的我就不介绍了。 Winform的MVC模式 image winform的MVC模式,主要是通过事件的方式来实现。 B、MVP架构模式 MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间 的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。 在MVC里,View是可以直接访问Model的!从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。 在MVC模型里,更关注的Model的不变,而同时有多个对Model的不同显示,及View。所以,在MVC模型里,Model不依赖于Vie w,但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的。 1、View和Model完全解耦,两者不发生直接关联,通过Presenter进行通信。 2、Presenter并不是与具体的View耦合,而是和一个抽象的View Interface耦合,View Interface相当于一个契约,抽象出了对应View应实现的方法。只要实现了这个接口,任何View都可以与指定Presenter兼容,从而实现了P Logic的复用性和视图的无缝替换。 3、View在MVP里应该是一个“极瘦”的概念,最多也只能包含维护自身状态的逻辑,而其它逻辑都应实现在Presenter中。 总的来说,使用MVP模式可以得到以下两个收益: 1、将UI和P Logic两个关注点分离,得到更干净和单一的代码结构。 2、实现了P Logic的复用以及View的无缝替换。 image 展示器层作为核心的控制,实现view和model之间的完全解耦。关于该架构设计的具体demo 后面来介绍 C、MVVM架构模式 MVVM是Model-View-ViewModel的简写。 微软的WPF带来了新的技术体验,如Sliverlight、音频、视频、3D、动画……,这导致了软件UI层更加细节化、可定制化。同时,在技术层面,WPF也带来了 诸如Binding、Dependency Property、Routed Events、Command、DataTemplate、C ontrolTemplate等新特性。MVVM(Model-View-ViewModel)框架的由来便是MVP(Model-View-Presenter)模式与WPF结合的应用方式时发展演变过来的一种新型架构框架。它立足于原有MVP框架并且把WPF的新特性揉合进去,以应对客户日 益复杂的需求变化。 MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大优点 1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。 2. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。 3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代码。 4. 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。 1. 视图(View) 视图负责界面和显示。它通过DataContext(数据上下文)和ViewModel进行数据绑定,不直接与Model交互。 可以绑定Behavior/Comand来调用ViewModel的方法,Command是View到ViewModel的单向通行,通过实现Silverlight提供的IComand接口来实现绑定,让View触发事件,ViewModel来处理事件,以解决事件绑定功能。 2. 视图模型(ViewModel) 视图模型主要包括界面逻辑和模型数据封装,Behavior/Command事件响应处理,绑定属性定义和集合等。它是View和Model的桥梁,是对Model的抽象,比如:Model中数据格式是“年月日”,可以在ViewModel中转换Model的数据为“日月年”供View显示。 实现视图模型需要实现Silverlight提供的接口INotifyPropertyChanged, INotifyPropertyChanged接口用于实现属性和集合的变更通知(Change Notifications)。使得在用户在视图上所做的操作都可以实时通知到视图模型,从而让视图模型对象有的模型进行正确的业务操作。 View的代码隐藏(Code-Behind)部分可能包含界面逻辑或者应用逻辑的代码,这些代码会很难进行单元测试,应根据具体情况尽量避免。 3. 模型(Model) Model与MVC模式一样,Model用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。它具有对数据直接访问的权利,例如对数据库的访问,Model不依赖于View和ViewModel,也就是说,模型不关心会被如何显示或是如何被操作, 模型也不能包含任何用户使用的与界面相关的逻辑。Model在实际开发中根据实际情况可以进行细分。比如在广州市城乡规划资源平台就将Model将Service和Reposiroty结合为WCF服务由ViewModel进行调用。 一般来说实际的项目中会采用以下的模式来做,而不是直接采用传统的MVVM模式,而是结合MVP或MVC模式来做。 image 上图中的P层是整个项目的核心,负责处理View层显示的数据来源及用户操作的响应的处理,通过绑定viewModel中的command的处理来与后端服务进行交互,展示器层会调用后端的WCF服务来读取数据,也就是读取DataModel 然后修改View Model。通过WPF提供的通知机制,来修改view的呈现。 MVC、MVP、MVVM对比 标题 MVC MVP MVVM 特点 高内聚、低耦合-一个控制器可以控制多个视图 高内聚、低耦合-解决MVC中View依赖Model的问题 高内聚、低耦合-解决winform中存在的问题。解决view和Model之间的依赖,屏蔽view改变带来的影响。 应用场景 前端与后端交互架构设计(CS或BS) 前端与后端交互架构设计(CS或BS) 前端与后端交互架构设计-WPF或Web通过js实现 五、结束语 通过上面软件架构模式的介绍,大家对这些软件架构的模式有了一定的了解,后面的关于分层中篇、后篇就是结合一些具体的案例来进行代码的编写的讲解和实现。当然如果大家有比较感兴趣的议题,也请提出来,可以根据这些议题,然后将上面介 绍的这些模式来去实现。 也欢迎大家针对我提出的这些思路进行讨论,提出不同的看法和想法,另如果需要更深层次的讨论,可以QQ与我联系。 关于上面介绍的只写架构模式,我已经全部实现,如果需要相关的技术支持,请找我,或者您有什么建议或意见,都请联系我。 本文转自何戈洲博客园博客,原文链接:http://www.cnblogs.com/hegezhou_hot/p/3489795.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

每日一博 | DDD 落地实践 - 架构师眼中的餐厅

本文以餐厅场景为叙事主线,以领域驱动为核心思想,结合架构设计与功能设计方法论。是从领域分析到落地的全过程案例,内容偏重于落地,因此不乏一些探讨,欢迎指正。 文章较长、全程干货、耐心读完、必有收获。 本文不针对餐厅的实现细节,重在探讨设计思想和方法。 1、领域设计 让我们抛开技术人员的本能技术视角、站在纯业务视角来分析领域问题。 领域设计的核心是分而治之,目的是实现业务领域的自治性。 就像你平时不会将枕头和被子放在厨房或卫生间一样,你的床上不会放着大米白面,否则你想睡觉是一件很复杂的事情,软件系统也是如此,这就是我们要解决的问题。 1.1 宏观流程 假如我要设计一个餐厅,由于分而治之的需要,我会首先从宏观流程去分析,可以帮我们迅速找到重要的区域。  因此会得到几个明确的行为区域,我将餐厅划分为“菜品域”,“订单域”,“厨房域”,“用餐域”,这是业务级别的领域划分,后续应该针对每个区域单独分析。 产出物是:宏观流程和参与角色 1.2 统一语言 语言贯穿于整个开发过程,从需求分析到设计、从设计到编码,因此好的语言非常重要,好的语言体现了清晰的业务概念。 在这个阶段,我们需要通过梳理,找到业务中都有哪些实体与行为,对其做一些归纳。我们的核心问题是:“谁”通过什么“行为”影响了“谁”,其中的三个要素分别是:角色、行为、实体。我的建议是先找到 “角色”、“实体”、“行为”,并对其归类,我常常关注角色以及具体身份、实体以及实体实例,功能以及包含的重要步骤。 角色:是施事主语、是名词,是主动发起行为的一类实体。 行为:是动词、是做了什么事情,是行为本身。 实体:是名词,是除“角色”之外的其他实体。 推荐使用脑图画出来,我认为归纳后的脑图有助于我们识别根本要素,有利于抽象。 产出物是:名词、概念定义、相关脑图。  1.3 用例分析 在这一步、我们使用相对宏观的分析,不需要进入用例的细节分析,掌握角色与行为之间的关系,理清谁在做什么,角色的职责差异是什么。 产出物:用例图 以做菜为例,如图 1.4 领域划分 我们在分析宏观流程时,划分了几个行为区域,但那是业务级别的。在那基础之上,我们需要拉进某个区域的视角,再结合之前的用例分析,按照“功能相关性”、“角色相关性”进一步划分领域。 功能相关性:是用例与领域之间的关系,任何业务的领域都是由一套用例组成的,所以领域划分以功能相关性为主,例如与做菜相关的用例都应该归属于厨房,所以我们确认了厨房域,确认了厨房域包含的用例,这是很自然的事。 角色相关性:其次是角色,常用于划分子域,某个区域涉及多个角色参与,可以按照角色的分工,拆分为多个子域,从而满足不同角色的个性化需要。例如厨房的采购人员负责买菜、刀工负责切菜、大厨负责烹饪。我们就会考虑将厨房划分为“采购域”、“加工域”、“烹饪域”。 通常来说,子域不具备独立的问题空间,不会作为独立的领域存在。 产出物:领域、子域 以厨房域为例,如图  1.5 领域建模 这是大家比较熟知的阶段,重点分析实体与领域之间关系(领域聚合),实体与实体的关系(OO聚合)。 领域模型是实现功能的基石、需要有对功能的本质理解,才能找到最核心的实体,实体之间的OO聚合关系决定了功能的扩展性,OO聚合是最重要的核心点。  组合、聚合 聚合(aggregation):聚合关系是一种弱的关系,整体和部分可以相互独立。 组合(composition):组合关系是一种强的整体和部分的关系,整体和部分具有相同的生命周期。 可以使用如下案例,既能表达领域聚合,又能表达OO聚合的关系。   产出物:聚合、实体、值对象、实体的属性 (领域服务和事件在后续的功能设计中提供) 1.6 领域上下游 领域上下游关系,不是领域的依赖关系,依赖关系指的是能力的依赖,是共用了某些能力,依赖关系是固定的。领域上下游关系,也不是调用关系,调用关系是与用例相关的,并非描述领域处境的。 领域上下游关系指的是影响力的关系,上游影响下游,影响力分为“逻辑影响”和“数据影响”,一般说来我们更应该关注“数据影响”,所以领域上下游关系是一种数据流向的限定,是业务发生的顺序限定,用于规定该领域所使用的数据,是下游领域依赖上游领域“准备就绪”的体现。合理的上下游限定,有助于减少领域之间的不必要依赖,有利于数据的复用并减少重复计算。 领域上下游是与场景相关的,并不是一成不变的,不同的场景存在不同的上下游,各场景应该独立说明。 产出物:各场景的上下游说明 例:在【菜品管理】场景下 如果厨房的某些食材不足了,或者某个厨师休假了,就会影响到菜品的展示,从而影响到客户的订单。 例:在【客户消费】场景下  客户的订单、影响厨房生产的菜,从而影响刀工的行为,也影响到了采购。 请对比下面两个图,用于理解领域的上下游  实际上,厨师不应该依赖采购人员的采购功能,也不依赖刀工的切菜功能,他只是依赖“初加工食材”而已,而“初加工食材”就是被处理好的数据,厨师在做饭时,“初加工食材”就已经被处理好了,上面的图例只是为了说明一个关于领域上下游的问题,这是业务发生顺序以及数据来源的问题。 我们常常使用领域事件串联业务流程,在使用领域事件时,不止要关注点对点的解耦,更应该使业务流程符合领域上下游限定,让各个领域独立运行,减少领域之间的功能依赖,降低领域之间的耦合,减少业务变化带来的影响。 2、架构设计 架构设计是为了解决软件系统复杂度带来的问题,找到系统中的元素并搞清楚他们之间关系。 架构的目标是用于管理复杂性、易变性和不确定性,以确保在长期的系统演化过程中,一部分架构的变化不会对其它部分产生不必要的负面影响。这样做可以确保业务和研发效率的敏捷,让应用的易变部分能够频繁地变化,对应用的其它部分的影响尽可能地小。 架构设计三原则:合适原则、简单原则、演化原则 2.1 分层架构 我们需要按照 接口层、领域层(领域用例层、领域模型层)、依赖层、基础层 构建架构模型。 接口层:为外部提供服务的入口,是适配层的北向网关。不实现任何业务逻辑,也不处理事务,是跨领域的,是流程编排层,是门面服务。 领域用例层:是领域服务层,是领域用例的实现层、隶属于某个领域、是业务逻辑层,是事务层,业务逻辑应该在这层完整体现,不要分散到其他层级。 领域模型层:是领域模型(实体、值对象、聚合)的所在位置,专注于领域模型自身的能力,不包含业务功能,可以处理事务,是原子化的能力,是领域对象的自我实现。 依赖层:是连接外部服务的出口,是适配层的南向网关。包括仓储,端点、RPC等,主要作用是领域和外部解耦,用于保持领域的独立性,是跨领域的。 基础层:与业务无关的,与领域无关的,通用的技术能力,技术组件等。 2.2 架构映射 架构的视角,从大到小依次是:系统->应用(微服务)->模块(包)->子模块 这样的从大到小的层级。 业务领域映射:我们将划分好的领域,按照对应的视角映射为对应的元素,领域模型映射到架构模型时,应该是视角对等的,如果餐厅是系统、那么厨房就是应用,如果餐厅是应用、那么厨房就是模块。也应该层级匹配的,将用例的实现映射到用例层,将领域模型的实现映射到领域模型层。 技术和抽象问题:有时候、业务领域分析不能体现那些共性的技术问题,所以需要适当结合技术视角,可能需要对领域模型微调。同时、我们需要找到共同需要的基础能力,例如“水”、“电”、“煤气”等等,将这些作为额外的考虑因素,要做到业务问题与技术问题解耦,不要将技术问题和业务逻辑揉成一团。 领域设计,类似餐厅设计师,他设计餐厅有几个区域,区域的用途是什么。 架构设计,类似建筑设计师,他设计如何走水电煤气、如何施工等。 产出物:分层架构图 以厨房为视角,其架构如下  以餐厅为视角,其架构如下  分层架构图,体现逻辑上的层级分布,而不是代表组件的具体含义,组件是应用还是模块、需要结合实际情况而定。 2.3 必要的约束 1、分层架构越往下层就越是稳定的:下层是被上层依赖的,下层不可以反向依赖上层(扩展点除外)。因为分层架构的核心原则是将容易变化的逻辑上浮,将共性的、原子化的、通用的逻辑下沉,被依赖的下层应该是稳定的,这要求上层承接更多业务变化。下层离开上层应该是可以独立存在的,例如在接口层定义的DTO不可以在下层被使用,但领域层定义的实体可以被上层使用。 2、在使用充血模型时,应该符合面向对象编程原则:不要随意的将一些能力都充到领域实体模型中。以“菜”为例,重量和规格是“菜”的自身的属性,激发味蕾是“菜”的能力,“菜”可以维护自身的持久化状态。但是、请注意、“菜”不可以“炒菜”,因为“炒菜”的时候,“菜”还没有出现呢,“菜”不是自己的上帝,“菜”需要被做出来,所以“菜”被做出来之前是没有“菜”的,这是个时间上的概念,不要错把“炒菜”的能力放在“菜”的身上。“炒菜”用到的“水+电+气+食材+调料+厨具”不应该是“菜”的属性范围,这些元素都在“厨房”的范围中,不要让领域的模型包含不属于自身的元素,领域的实体模型只是领域的一部分,只用于实现通用的模型能力。 3、接口层和依赖层是与领域无关的:他们是与技术相关的层级,不属于任何领域,这两层不能包含业务逻辑。有时候我们可以把接口层拆为两层(接口层+应用层),也可以把依赖层拆分为两个(模型依赖、服务依赖)。 4、领域层是与环境无关的:无论某个领域是应用还是模块,都应该具备独立的用例层和独立的模型层,即使多个领域在同一个应用当中,也要按照他们是分别独立去看待,无论某个领域是应用还是模块,领域对外部的交互,不可以绕过依赖层和接口层。 5、领域应该是最小完备的:把一个领域拆分为子域、子子域、子子子...... 无限拆分,拆分到一定程度之后,某个子域就不完整了,不完整的子域是不可以独立存在的。拆分不不够或者过度拆分,都是不符合低耦合高内聚原则的。当一个领域的内部子域不具备独立性时,他们之间不必严格解耦,不需要通过依赖层访问本领域的其他子域,他们之间可以直接调用。 6、领域服务层就是领域用例层:他们俩是同一回事儿,都是用于实现领域内的用例的。不要将领域服务与领域用例视为两个独立的层,也不要将领域服务与领域模型视为同一层,否则会导致逻辑的分散(一部分在领域服务层、一部分在领域模型层、还有一部分可能在用例层),也会导致每个层的职责不明确,容易搞乱。如果将业务逻辑写在领域模型中,会导致业务逻辑进一步下沉,业务逻辑的不确定性太大,是不适合下沉的,是违反分层架构原则的。领域模型对应的是实体、领域服务对应的是用例。 7、领域用例层只能承接符合自身领域的用例:我们划分出领域的目的,就是为了区分每个领域的职责所在,因此他们必须严格按照职责办事,我们在之前已明确了用例和领域之间的关系,需要严格遵守。 8、领域模型层遵循最小依赖原则:只可以依赖必要的资源,必要资源指的是领域模型实现自身能力需要的资源,不包括实现业务逻辑包含的资源。例如领域模型需要依赖DB完成持久化,可以依赖数据访问资源,但不应该依赖其他领域资源、不可以依赖RPC资源等。 2.4 微服务划分 服务划分以领域划分为参考,主要看我们要拆分到什么粒度,这 应该符合低耦合高内聚原则,不破坏领域实体的聚合关系。 产出物:微服务 例如餐厅:是有必要拆分的,餐厅的“菜品域”,“订单域”,“厨房域”有独立的问题空间。 例如厨房:是没有必要拆分的,厨师与刀工的耦合非常高,他们都在做饭,分开之后是不完整的,分开就是没有必要的。 所以餐厅被拆分为:厨房(Kitchen)、菜品(Category)、订单(Order)三个微服务。 基于此、我们单独拿出餐厅门面服务作为接口层应用,再单独拿出餐厅基础服务作为水电煤气的应用。 一般情况下,依赖层不会作为单独的服务提供,会被以组件的形式嵌入到其他服务中。 3、功能设计(用例实现) 如果说领域设计是餐厅的设计师、架构设计是餐厅的建筑师、那么功能设计就是餐厅的厨师或服务员。 任何设计都要落地到功能设计,如果厨师不守规则,偏偏要去洗手间洗菜,最后的结果依然是一团乱,最终会导致设计无法落地。 功能设计是实现 “面向扩展开放、面向修改关闭” 的途径,是指导研发落地必备环节。 3.1 功能的概念 功能迭代时,功能会发生一些变化,所以他的含义是可能变化的,所以我们需要再次审视功能的概念,及时加以调整。 例如、我们实现了一个“做蛋炒饭”的功能,后来又实现了一个“做辣椒炒蛋”的功能,那么我们应该将功能升级为“炒菜”,甚至是“制作菜品”等。 明确功能的概念,是功能设计的前提。 产出物:更新语言库,更新脑图 3.2 用例的位置 我们在领域分析章节,已明确了用例与角色的关系,用例与领域的关系。 然而一个新功能的加入,我们仍然要再次评估,以确保他处于正确的位置。 产出物:更新用例图 3.3 事件风暴 我们需要深入功能的细节,首推的方法是事件风暴,适用于解构复杂功能。 事件风暴的作用并不限于功能分析,只是我觉得很适用于功能分析,事件风暴的一张图包含很多内容,正好是功能设计所需要的。 将功能拆分为多个子功能(步骤)。(在后续使用) 确认参与该步骤的角色和领域。(在后续的3.6章节落地) 确认步骤的串联流程和领域事件。(在后续的3.6章节落地) 确认参与该步骤的领域实体。(在后续的3.7章节落地) 产出物:事件风暴模型 3.4 用例分析 我们暂且收回思路,首先要关注共性和差异问题,以确保功能的扩展性。 确认用例的泛化+差异点,实现功能的扩展。 寻找共同包含的步骤,实现逻辑的复用。 产出物:用例分析图 例:制作菜品(做大拌菜、做铁锅炖、做炒鸡蛋、做蒸米饭、做炒米饭) 3.5 用例实现类(领域服务类)结构图 专注于用例层的类设计,实现“面相修改关闭,面相扩展开放”。 用例的类结构图是用例分析图的一种映射。 出物:用例层的类结构图  3.6 用例流程图 我们接回思路,更进一步,将事件风暴模型落实到代码层面。 我们将步骤分配到实现类中、步骤就是该类的一个方法,进一步明确由哪个类和方法来实现该步骤,从而就规定了步骤所在的领域。 我们将步骤和领域事件串联起来,规定了业务实现流程。推荐使用泳道图表达上述内容。泳道的纵向组件是用例的实现类。 这是真实业务流程的映射。 产出物:用例流程图 以炒鸡蛋为例,其用例流程图如下  3.7 活动图(时序图) 我们进一步将事件风暴模型落实到代码层面,我们使用时序图,体现依赖和调用关系,规定了步骤与领域实体模型的关系,进一步说明用例是如何实现的。 这时候,为了简便、我们可以收起领域服务类(用例层)的泳道。 产出物:时序图、活动图  试想一下、假如把业务逻辑放在领域模型当中(例如聚合),如何实现“面相扩展开放、面相修改关闭”呢? 4、编码实现 编码实现...... 我决定还是...... 偷个懒吧...... 哈哈哈。 但是我们回顾一下之前的内容,是否足够了?不同的研发人员依照设计去编码,是否会写出不一样的代码? 最后、我们的目标是“解决软件复杂度带来的问题”,而实现这个目标的途径是“设计指导研发落地”。 本文分享自微信公众号 - 京东云开发者(JDT_Developers)。 如有侵权,请联系 support@oschina.cn 删除。 本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册