1、架构分层模型适配吴雪峰201811 有效防止架构腐化实践CONTENTS01DDD分层参考架构02严纪律 防腐化 分层模型适配03分层模型适配实例DDD分层参考架构DDD分层参考架构给用户提供界面,关注用户交互和体验前端应用API服务业务领域基础设施为前端应用提供API服务,关注事务和分布式等技术性问题领域模型和领域逻辑,关注业务概念。访问外界系统(调用外界系统)的技术相关实现。后台服务前端应用分层依据:干系人和技术点DDD分层参考架构-前端应用DDD重点关注后台业务服务,不解决前端交互问题前端界面API服务业务领域基础设施前端应用前端应用干系人:终端用户 诉求:良好的用户体验 技术点:人机交
2、互设计和实现UX关注的层DDD分层参考架构为前端和第三方应用提供API服务,关注服务编排,事务和分布式等前端应用应用服务业务领域基础设施干系人:应用开发人员 诉求:灵活易使用的API 技术点:性能,事务,分布式,安全等非功能性需求API服务工作内容:接收外部请求并响应:如HTTP请求,消息处理 事务管理 认证 缓存 日志 异常处理 配置 Session技术人员关注的层腐化案例:大量业务逻辑堆积模型:View Object Resource ModelDDD分层参考架构领域模型和领域逻辑,关注业务概念。前端应用API服务领域模型基础设施干系人:业务领域专家,业务领导 诉求:表现业务概念和实现业务
3、价值 要点:业务建模和复杂性管理工作内容:建立业务模型,并体现在代码上 管理模型复杂度,适度拆分模块 实现业务逻辑业务人员关注的层业务领域腐化案例:亏空 大量技术术语业务人员完全看不懂 模型:应用服务 跨Bond Context DTO 领域服务 跨聚合 聚合 实体 仓库 事件DDD分层参考架构访问外界系统(调用外界系统)的技术相关实现。前端应用API服务业务领域基础设施干系人:外界系统 诉求:稳定调用外部系统 技术点:使用和适配外部系统模型,隔离和快速诊断错误工作内容:学习和使用外部系统,如数据库,邮件系统 适配外部系统模型,如SQL映射到模型对象 技术人员关注的层基础设施腐化案例:业务逻辑
4、和外部调用逻辑混合 如一个方法里即处理业务逻辑又调用SQL模型内容:PO 第三方Protobuffer 第三方SDKDDD分层参考架构 Java技术视角前端应用API服务业务领域基础设施技术人员关注的层技术人员关注的层业务人员关注的层UX关注的层Web,Spring Boot,Kafka,Redis,JTA,两阶段提交,SSO,服务注册ORM,SQL DB,NoSql,服务发现尽量少依赖技术框架,让业务人员也看得懂代码参考架构对比https:/ DDD NLayered.NET 4.0 Architecture 前后端分离,完全剥离曾现层为了简便,合并Web Service和Applicati
5、on层Controller概念下移到Application层参考架构对比微服务架构https:/ Gateways,HTTP Client和ORM领域层对应 Service Layer,Domain和RepositoriesDDD分层架构对比前端应用API服务领域模型基础设施业务领域DDD分层架构对比Clean Architecture前端应用API服务领域模型基础设施业务领域https:/ Architecture前端应用API服务领域模型基础设施业务领域https:/en.wikipedia.org/wiki/4%2B1_architectural_view_modelclass diag
6、rams,and state diagramsactivity diagramPackage diagramuse casesdeployment diagramDDD分层架构对比 Enterprise architecture framework 前端应用API服务领域模型基础设施业务领域如何有效防止架构腐化架构腐化是系统开发过程中非常头疼的事情。使用DDD分层架构参考实践,可以在系统初始设计的时候领域明确出来。但问题是开发过程中不知不觉层次模糊,架构师有没有实践能有效阻止架构腐化?分层模型适配如何从一盘散沙到百万雄师百万雄师的铸造秘密 踢正步 叠被子知行合一 每个人日常就能做到 在日常最细
7、微处抵抗懒散腐化 坚持塑造纪律DDD分层架构 不同模型适配(名词)资源模型:应用想要一把查询获取所有信息,一个操作做完业务前端应用API服务业务领域基础设施业务模型:精确表达一个业务概念,分治管理复杂度存储模型:性能最优化DDD分层架构 不同模型适配(动词)HTTP方法:POST GET PUT DELETE前端应用API服务业务领域基础设施业务操作:注册 注销 登录 退出 修改 撤销存储动作:INSERT UPDATE DELETEDDD分层架构治理HTTP方法:POST GET PUT DELETE前端应用API服务业务领域基础设施业务操作:注册 注销 登录 退出 修改 撤销存储动作:IN
8、SERT UPDATE DELETE资源模型:应用想要一把查询获取所有信息,一个操作做完业务业务模型:精确表达一个业务概念,分治管理复杂度存储模型:性能最优化不同层级的模型坚决隔离,严格一对一翻译映射,不准复用DDD分层架构治理HTTP方法:POST GET PUT DELETE前端应用API服务业务领域基础设施业务操作:注册 注销 登录 退出 修改 撤销 查看 查询 验证码 激活存储动作:INSERT UPDATE DELETE动词翻译映射create retrieve update deletesavefindDDD分层架构治理前端应用API服务业务领域基础设施名词翻译映射资源模型:用户想
9、要一把查询获取所有信息,一个操作做完业务业务模型:分解的业务概念存储模型:性能最优化HTTP字符文本Json视图对象一个用户请求一个视图对象多个固定的业务概念表达业务规则SQL:无冗余字段设计,主外键关联 NoSQL:冗余字段设计,无Schema宽列DDD分层架构治理前端应用API服务业务领域基础设施名词翻译映射View ObjectHTTP字符文本JsonData Transfer ObjectDomain Object(Entity,Value Object)Persist Object/Protobuffer Object/View ObjectProtobuffer ObjectBin
10、aryHttp Restfull APIRPCDBRPCJsonHTTP字符文本Http Restfull APIData Transfer Object分层模型适配实例架构治理中的踢正步用户账户注册和激活案例-用户注册和激活填写注册信息HTTP POST 注册信息处理注册信息保存注册信息发送注册激活码填写激活码HTTP POST 激活码验证激活码保存激活信息案例-用户注册-1前端应用API服务业务领域基础设施POST /users mobile:“”,password:29WLV742createUser(registerInfo)register(account,pa
11、rterId)CREATE TABLE account(uid varchar(32)NOT NULL,login_id varchar(128)DEFAULT NULL,password_hash varchar(128)DEFAULT NULL,name varchar(100)DEFAULT NULL,activated bit(1)DEFAULT NULL DEFAUT 0,created_timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,password_expiration timestamp DEFAULT NULL,P
12、RIMARY KEY(uid);class RegisterInfo(mobile:String,password:OptionString)class Account(uid:OptionUUID,mobile:String,password:OptionPassword,activated:Boolean=False)class Password(passwordPlain:String)def passwordHash()=encode(password)def passwordExpiration()=save(account)一对一字段映射翻译到业务模型从业务业务模型 翻译出存储模型
13、案例-用户注册-2前端应用API服务业务领域基础设施register(account,parterId)CREATE TABLE verification_token(verification_id bigint(20)unsigned NOT NULL AUTO_INCREMENT,account_uid varchar(32)NOT NULL,token varchar(128)NOT NULL,expiry_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,verify_type varchar(32)NOT NULL,PRIMARY K
14、EY(verification_id),CONSTRAINT account_uid_fk FOREIGN KEY(account_uid)REFERENCES account(account_uid)ON DELETE CASCADE);save(verificationToken)从业务业务模型 翻译出存储模型通过短信发送注册激活码verify(account)save(account)notifyVerificationToken()class VerificationToken(account:Account expireToken:String,expiryTime:Date,ver
15、ifyType:String)案例-用户注册-动名词个数总结前端应用API服务业务领域基础设施动词名词应用服务11领域模型13基础设施32案例-用户激活前端应用API服务业务领域基础设施PUT /users/activate uid:“d219-xww0-13af-b021”,token:“23u3$z4o6799updateActivate(activateInfo)activate(VerificationToken)class ActivateInfo(uid:String,token:String)一对一字段映射翻译到业务模型从业务业务模型 翻译出存储模型save(account)UP
16、DATE account SET activated=account.activated delete(verificationToken)class VerificationToken(account:Account expireToken:String,expiryTime:Date,verifyType:String)案例-用户激活-动名词个数总结前端应用API服务业务领域基础设施动词名词应用服务11领域模型12基础设施22原则即使不同层的模型字段完全一样,也要求各自创建。架构师职责提炼概念 管理复杂性坚持原则 但不增加工作量import mons.beanutils.BeanUtils
17、;UserBean newObject=new UserBean();BeanUtils.copyProperties(newObject,oldObject);public class SystemUser private String firstName;private String lastName;private String phone;private String addressLine1;private String addressLine2;private String city;private String state;private String zip;/getters
18、and setters /.public class User private String first;private String last;private String address;private String city;private String state;private String zip;private String phone;/getters and setters /.坚持原则systemUser.setFirstName(user.getFirst();systemUser.setLastName(user.getLast();systemUser.setPhon
19、e(user.getPhone();systemUser.setAddressLine1(user.getAddress();systemUser.setAddressLine2(user.getAddress();systemUser.setCity(user.getCity();systemUser.setState(user.getState();systemUser.setZipcode(user.getZip();看似工作量多 其实降低了复杂度(纠结)保持User模型的纯洁 降低了复杂度如果从概念上User模型就是只有一个address守护原则-CI检查 分层架构单元测试https:
20、/www.archunit.org/import com.tngtech.archunit.junit.AnalyzeClasses;import com.tngtech.archunit.junit.ArchTest;import com.tngtech.archunit.lang.ArchRule;import static com.tngtech.archunit.library.Architectures.layeredArchitecture;AnalyzeClasses(packages=com.tngtech.archunit.example)public class Layer
21、edArchitectureTest ArchTest static final ArchRule layer_dependencies_are_respected=layeredArchitecture().layer(“Applications).definedBy(com.tngtech.archunit.example.applications.).layer(“Domain).definedBy(com.tngtech.archunit.example.domain.).layer(Infrastructure).definedBy(com.tngtech.archunit.exam
22、ple.infrastructure.).whereLayer(Applications).mayNotBeAccessedByAnyLayer().whereLayer(Services).mayOnlyBeAccessedByLayers(Applications).whereLayer(“Infrastructure).mayOnlyBeAccessedByLayers(Services);守护原则-CI检查 分层模型单元测试https:/www.archunit.org/import static com.tngtech.archunit.lang.syntax.ArchRuleDef
23、inition.classes;import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;AnalyzeClasses(packages=com.tngtech.archunit.example)public class DaoRulesTest ArchTest static final ArchRule DAOs_must_reside_in_a_dao_package=classes().that().haveNameMatching(.*Dao).should().resideInAPacka
24、ge(.dao.).as(DAOs should reside in a package.dao.);ArchTest static final ArchRule entities_must_reside_in_a_model_package=classes().that().resideInsideOfPackage(“.domains.model.).should().resideInAPackage(.model.).as(“Domain models should reside in package.model.);ArchTest static final ArchRule only_DAOs_may_use_the_EntityManager=noClasses().that().resideOutsideOfPackage(.dao.).should().accessClassesThat().areAssignableTo(EntityManager.class).as(Only DAOs may use the +EntityManager.class.getSimpleName();