最近连续做了两个新项目,借着新项目的机会,重新审视一下之前一些实践方法,进而寻求一下背后的理论支撑
新项目开始,首先一个就是会新建一个project,那么这个project怎么分层,怎么创建module,怎么分包呢?
经典分层
以传统方式,经典的MVC分层,就controller,service,model
找来一张servlet时代的经典处理流程,虽然技术手段日益更新,但处理流程是一样的
抽象一下,经典的分层就是:
现在大多数系统都是这种分层结构。JavaBean里面只有简单的get和set方法,被很多人鄙视的“贫血模型”;业务逻辑变得复杂时,service会变得很臃肿,出现很多的“上帝类”
回想一下,我们的所有的代码都写在ServiceImpl里面,前几行代码是做validation的事,接下来几行是做convert的事,然后是几行业务处理逻辑的代码,穿插着,我们需要通过RPC或者DAO获取更多的数据,拿到数据后,又是几行convert的代码,在接上一段业务逻辑代码,然后还要落库,发消息…等等
这也是事务脚本开发方式,横行于世。数据与行为被分离。
简单业务这样开发是合适的,但业务一复杂起来,需求频繁变更后,就没人能理解任何一段代码,业务逻辑和技术细节糅杂在一起,业务领域模型没法清晰表达出来,开发人员只知道怎么处理了数据,但背后的业务语义完全不知道
其实呢?还有很多的包,如config,common,core等等, 如果使用了一些中间件,如rabbitmq,还会相应创建上对应的包,简单点可能就被放在了service包下
从有了maven之,module概念更加显现化
代码语言:javascript复制<modules>
<module>service</module>
<module>common</module>
<module>core</module>
<module>test</module>
</modules>
我们的那么多包有了更加明确的地方放置,不再是直接放置在工程目录下
由于上面的这些问题 ,我们似乎可以指出经典的三层架构的弱点:
- 架构被过分简化,如果解决方案中包含发送邮件通知,代码应该放置在哪些层?
- 它虽然提出了业务逻辑隔离,但没有明确的架构元素指导我们如何隔离
DDD
虽然技术日新月异,但大多仅仅是技术,带了实现的便利性,但对于业务层次,更多的还是经验。随着业务的复杂性提升,系统的腐化速度和复杂性呈指数上升。
DDD带了很多的认知的改变,最大的好处是将业务语义显现化,不再是分离数据与行为,而是通过领域对象将领域概念清晰的显性化表达出来
当然这世间并没有银弹,但至少能给我们带来一种改进经典分层的理论支撑
DDD中带来了Repository概念,以及基础设施层,再结合【DIP原则】,可以把三层结构变成
再细看一下Controller,这一层,做些什么呢?
轻业务逻辑,参数校验,异常兜底。通常这种接口可以轻易更换接口类型,所以业务逻辑必须要轻,甚至不做具体逻辑
但在现实中,有些更极端,在servlet时代,还做下HttpRequest转换成DTO,传入service,现在有了springmvc,struts2框架的转换,不需要转换了,那么controller成了一个透传层,直接调用service
这儿有两个问题,既然controller是透传,那有必要存在吗?controller调用的service,这个service指的服务是什么呢?
第一controller显然有必要存在,不在于业务,而在于技术实现。不管是http,rpc都得有个请求入口。像thrift可能会比其他的一些rpc框架例如dubbo会多出一层,作用和controller层类似
Tservice与controller的作用是一样的
第二service服务指的是什么?领域服务吗?如果一个复杂的业务,那么会跨越多个领域,自然需要多个领域服务。如果放在controller里面,也就是在controller里面去编排领域服务,如果切换到thrift,那Tservice就得重复
因此,此时需要另一个service,在DDD中就是应用服务
应用服务算是领域服务的facade,应用层是高层抽象的概念,但表达的是业务的含义,领域层是底层实现的概念,表达的是业务的细节
代码语言:javascript复制<modules>
<module>controller</module>
<module>application</module>
<module>domain</module>
<module>infrastructure</module>
<module>start</module>
</modules>
controller模块:restfull风格的少不了这层
application模块:类似以前的service包
domain模块:如果是事务脚本方式,domain模块就没有了
infrastructure模块:基础模块,类似之前的dao包,但这里面都是实现类,而像repository接口则在domain模块,还需要对应的convertor
模块里面各个包,可能需要按实践情况而定了,后面再从项目中抽取个archetype,使用maven直接生成