分层架构,不就是创建文件夹的艺术吗?注意:本文比较适合中大型项目,只要你对小项目满意。由于时代的原因,有些词汇的描述可能不是那么准确,欢迎指正。当我们开始一个新项目时,我们首先创建文件夹。哦,不,那我们是在做分层架构设计。该体系结构最终落在了现有的计算机操作系统上,以分层体系结构的形式呈现。毕竟硅基不如碳基。但是,为什么我们需要设计分层架构呢?通过层(Layer)分离不同的关注点。那么,我要开始唠叨了。基本思想:关注点分离,划分边界注:三层架构(controller-service-model)不等于MVC架构模式。由于其假等价性,导致了体系结构中的一系列错误。问题:落后的三层架构过去,我一直认为对于大多数项目来说,三层层次架构之外的部分是一团大泥球,即随机的代码组织方式。但是,我发现对于大多数项目来说,三层分层架构的服务也是一个大泥球。忘了三层分层架构的model层也是一堆大泥球。控制器好一点,但对于某些项目来说也有点烂泥。一大团泥巴指的是一个随机、杂乱的结构系统,它只是代码的堆积和拼凑,往往会导致很多错误或缺陷。在DDD+cleanarchitecture大行其道的今天,三层的分层架构已经不能满足现有应用的需求,甚至看起来一团糟。它有一些问题:统一管理是魔鬼,比如controller文件夹下一堆代码,到处都是模型。缺乏明确的职责分工,比如controller承担服务的职责,臃肿的服务,贫血模型,三层分层后的随机文件组织方式,比如Kafka等随机代码...但是为什么会这样?Responsibilities(orBoundedContext)有很多模棱两可的面向技术的架构模式,没有清晰明确的模型层……所以,我们有一些基本的解决方案,或者说套路。重新定义:消歧当我们谈论服务时,我们谈论的是同一种服务吗?当我们谈论模型时,我们是在谈论同一个模型吗?如果有两个不同的语法树,则语法是有歧义的。如果有很多不同类型的类,它们都放在模型包下。然后,您应该删除模型包并更改为更具表现力的内容,例如Entity、*Request、*Response等。同样,一旦您开始讨论一个名称,就该考虑它的歧义了。最后,您还需要有相关领域的名词列表。划分边界:面向业务的架构在开始之前,我们不得不这样说:微服务是一种面向业务的架构。微服务是一种面向业务的架构。微服务是一种面向业务的架构。所以,如果你的微服务事业部有几个不同的服务技术维度,那么你就需要反思了。所以,为了满足面向业务的架构,我们需要重新划分架构,采用横向+纵向的架构,将各个业务模板的代码聚合到各自的业务模板中,并凝聚大量的util和common顺便进入服务。相反,它们都基于其他低级模板。后面我们也可以尝试把单体应用拆分成微服务。然而,我们都不应该依赖低级模块,所以它就是......不应该依赖于低级模块,两者都应该依赖于抽象。这一点在大部分项目中都实践得比较好。毕竟有各种各样的*Service+*ServiceImpl。另外,为了达到这样的目的,对于采用DDD架构的应用,在我们领域层的限界上下文中,除了自身的entity,vo等,还应该有repository抽象。这样,我们的领域层不依赖于应用程序分层:DDD和cleanarchitecture那么,让我们来看一个问题。这是在GitHub上近15Kstar的Java语言编写的开源CMS中一个模块的代码目录:├──cms-admin/├──cms-common/├──cms-dao/├──cms-job/├──cms-rpc-api/├──cms-rpc-service/├──cms-search/└──cms-web/这是一个面向技术的应用架构。所以,当我想添加一个新功能时,我需要:向cms-dao模块添加一个模型和一个映射器向cms-rpc-service模块添加一个服务向cms-web模块添加一个控制器“Perfect”,没有问题。而且随着时间的推移,你现在有一个庞大的模型层,修改代码时需要跳转到不同的模块中。而不是快速修改相关代码。甚至,你不能采用微服务架构,你就是一个庞大的单体应用。为了挽救这样的项目,我们不得不尝试做一些事情。削减基础设施你的基础设施从开发完成到现在基本上没有变化,但是你的业务代码一直在变化。技术实现的变化原因与领域逻辑的变化原因明显不同,这导致基础设施和领域逻辑问题以不同的速率变化。——《领域驱动设计模式、原理与实践》当你来到一个项目,一眼就看到这么多基础设施相关的目录结构:├──controller├──interceptor├──jms├──rocketmq├──schedule└──taskoneday,我们又添加了一个Kafka,我们并没有添加新的文件夹,这样的层级设计看起来一点也没有条理。然后,当我们打开目录时,我们无法快速定位到我们的代码。除了目录中的基础设施包/层,它包含相关的基础设施代码。我们还需要考虑层上的单一职责,因为基础设施和业务代码之间的关系需要分离。所以,为了实现CleanArchitecture的伟大事业,你还需要一个抽象的接口,比如你要访问和存储业务相关的数据。然后抽象在您的域中,具体的RepositoryImpl实现在您的基础设施中。离心分离模型在一个系统中,你会有一些不同的模型:(PS:有些描述可能不准确,请指正)DO(DataObject)/PO(PersistantObject)对应数据库表结构。Query和Request用于查询数据。对外传输的对象:DTO(DataTransferObject)。业务层之间的数据对象:VO(ValueObject)/BO(BusinessObject)。访问数据库:DAO(DataAccessObject数据访问对象)。而我们想要的DDD中的实体Entity和其他的POJO(PlainOrdinaryJavaObject)不过都是模型,所以都扔到模型中了。。。或者bean中。。。结果,你有一个巨大的模型层。因此,在DDD或CleanArchitecture中,我们重命名了不同的模式:使用Command/Request作为输入参数。Command模式完成后需要发出相应的Event。使用Response/DTO/Representation作为返回结果。大家对Entity一直保持着一致的看法,用PO/DO作为数据库的存储模型,用DAO作为数据库的访问模型……不过,其实只要你不再使用models和豆子,你会得到更多类似的东西。聚焦领域,丰富行为完成了移动文件夹的操作,我们就来到了最麻烦、最复杂的部分。我们需要重构领域模型,重新规划模型和服务,让模型成为一个血脉贲张的模型。也许,你需要一个事件风暴来完成真正的事件风暴建模。不过步骤上不会有太多差异。重新分区包。也就是在不中断业务的情况下进行重构,让新的代码能够运行在新的架构上。分析抽象领域模型编写API测试,确保功能存在编写抽象接口,进行依赖倒置拆分服务层,重构代码。将行为绑定到域对象。在其他情况下,需要逐案分析。剩下的呢?共享业务逻辑可以由共享内核或其他模式处理。待补充。代码公共分层:创建公共共享组件的功能内聚导致了一系列问题,例如耦合、协调困难和增加的复杂性。当看到一个接一个硕大的common包的时候,我开始讨厌common、base、util这些该死的包,在它们的目录下统一管理的bean。我们真的用尽了它们,因此您应该重新访问您的项目代码。因此,从这个意义上说:复用和低耦合具有一定的互斥关系。baseunderbase以前重构过一个base项目的代码,就是这次重构让我意识到base并不是什么好东西。如果项目中已经抽取了一个base模块,那么这个模块下应该没有base等业务逻辑。而且,基地的东西还造成了一个问题,只要是共享的,就会想都没想就扔进基地。您将拥有一个包含各种抽象接口的基础包,但您需要一个更好的名称,例如概念,例如支持。简而言之,你不应该有一个基础模块,让开发人员去思考把新类放在哪里。极其臃肿的bean和model“这不是程序员的错,而是该死的Java语言”。相反,我开始思考一个问题,一个包(文件夹)下的文件数量是不是应该超过一定的数量?如果一个包下的类数量超过一定范围,那么就要考虑是否有职责相似的类。这部分可以参考上一节中的离心分离模型。什么不常见这个名字真的很烂,比base和model还差。一旦你从项目中移除一个公共模块,只会有一个结果,你会得到一个5G时代的jar包。即使在IDE中看到一段代码是灰色的,没有用到,也不敢轻易删掉。直到有一天,构建的普通包大小为10M或20M,只需要引用一个AESUtil,就发现问题:hello,world,本来几十K,现在变成几十M了。唐不事先创建公共模块,你可能不会有这个模块。任何水平分层拆分的应用程序在当今复杂的项目中都是不可靠的。谁用谁管理,而不是觉得通用就扔通用模块。它真的是一个实用程序吗?哦不,它是一个恶魔,因为它是一个util。你会不假思索地把逻辑扔进xxUtil,就像你会把所有的模型都扔进common/bean一样,直到有一天,你有一个庞大的基础,通用代码。大多数情况下,所有业务相关的Utils都有一定的问题,比如CaptchaUtil,要么将其分配到自己的上下文中,要么丢到domain/shared等共享上下文中,而不是和其他utils放在一起。而比如FileUtil、DateUtil、RedisUtil、JdbcUtil,这些可以说是infrastructure相关的部分,可以将它们分配到infrastructure/file或者infrastructure/date目录下,而不是统一管理这些utils。我们还有Coordinator、Builder、Writer、Reader、Handler、Container、Protocol、Target、Converter、Controller、View、Factory、Entity、Bucket等名称,如相关StackOverflow问题中所列。尝试杀掉Util,你会获得更多的职业,笑~。需要一个例子吗?查看SpringFramework源码层次结构,如SpringOrm:└──orm├──ObjectOptimisticLockingFailureException.java├──ObjectRetrievalFailureException.java├──hibernate5a/├──jpa/└──package-info.java或者spring-context下的目录层次:└──springframework├──cache│├──annotation│├──concurrent│├──config│├──interceptor│└──support├──context│├──annotation│├──config│├──event│├──expression│├──i18n│├──index│├──support│└──weaving它们都在自己的boundedcontext里面,维护自己的annotation、bean、support、i18n等包。分层架构重构那么,我们可以尝试做架构重构分析和诊断现有项目结构划分新分层架构功能测试使用抽象解耦依赖进行细粒度代码重构?不预先设计,定义原则和规范。从简单的设计开始,并在整个生命周期内改进架构。将统一的公用包TBC替换为多个公用包。结语那么,如何才能做好分层架构呢?根据经验。哦,不,DDD很好。
