Skip to main content

应用层

在领域驱动编码模式中,最容易出现的问题就是:把业务逻辑写到应用层中

应用层的作用可以用承上起下来形容,它的作用主要体现为:

  1. 是对系统用例的映射。
  2. 隔离领域层以保证不被污染。
  3. 实现其它系统行为。

由于系统业务主要是在领域层实现的,所以应用层的一个非常明显的特征就是:

1. 对系统用例的映射#

每个系统都会有用例,或者是用户故事,不管叫什么都好。这些都是对系统需求的一种表述。应用层应该是对系统用例的准确的映射。 比如:

  • 用例:新增一个雇员
  • 用例:查询雇员信息

那则在应用层,理所当然的会有其对应的映射方法:

fun addEmployee(employee:EmployeeDTO):Future<EmployeeDTO>
fun queryEmployeeInfo(username:String):Future<EmployeeDTO>

因此,应用层的一个明显特征就是它是对系统用例的映射

在理解这个点中,需要避免两个误解

误解一:应用层是实现业务的地方

应用层虽然是对系统用例的映射,但这不代表它是实现具体业务的地方。我在领域层中已经说过,实现业务的是领域层。

    override suspend fun createISVClient(isvClientDTO: ISVClientDTO): Future<ISVClientDTO> {        return try {            val isvClient = toISVClient(isvClientDTO)            val created = isvClient.createISVClient().await()            Future.succeededFuture(toISVClientDTO(created))        }catch (t:Throwable){            Future.failedFuture(t)        }    }

如上代码所示,应用层并不是业务实现的地方,很多时候,它只是简单的调用领域层的业务逻辑而已。

误解二:为什么不是controller层或REST API层

有些人可能会觉得,为什么不是REST API层是对系统用例的映射。

的确,从某种角度来说,看起来REST API好像也是对系统用例的映射 ,比如新增雇员,就会有一个新增雇员的REST API。以此类推。

但实际上,我们要把REST API这样的层,视为UI或协议层。这是什么意思?

也就是把REST API视为应用层的一种公开协议或UI,应用层的用例,可以通过REST API来实现,这是因为我们现在主流的实现方式是REST API,但这不代表就只有API对吧,实质上,服务公开的方式除了REST API还是RCP远程调用等其它很多方式。

所以,REST API这样的层,视为UI或协议公开更为妥当。

2. 隔离领域层以保证不被污染#

领域行为某种程序上与系统是独立的两种事物。

领域行为是业务行为的体现,与它密切关联的并不是系统,而是业务本身。比如OA系统,公司的OA业务是如何运转的,这个才真正决定你的领域层的代码与逻辑是什么样的。至于OA系统是怎么样的,这个不能决定或影响领域层的逻辑。

如果领域层的行为是受系统影响,这就有点本末倒置了。

某种程序上来说,领域行为是恒定的,而系统是多变的。

我们可能以WEB来展现,也可能以APP等,这些是系统,不管是以WEB还是APP,界面如何,体验又有何差别,但入职一个新雇员这个业务是恒定不变的。

因此,应用层有个非常重要的作用,就是隔离领域层。

系统的需求由应用层来承担,应用层将领域层放置它的保护之下,这样就相当于一个适配器,使得用例是符合系统需求。

这样,当你把服务公开给第三方时,第三方就接触不到你的领域逻辑与代码了。

3. 实现其它系统行为#

在我们的系统中,除了业务以外,还有很多非业务的行为,最典型的比如日志,缓存这一类的行为。

这些行为对系统是必不可少的,但实质它本身与业务并无太多关联。无论缓存存在不存在,查询或新增雇员这个业务都是这样运作的。

这样,这一类的行为将其放在领域层,就非常不合适了。

所以,在应用层中实现这一类的行为,就是最妥当的了。