Skip to main content

领域层

业务与非业务

在进行领域层开发前,你需要清晰的知道一个概念,什么叫业务行为,这是你知道该把哪些代码放在领域层,哪些代码放在非领域层的关键。

与系统核心业务相关的就是业务行为,与系统核心业务无关的,我们可以纳为系统行为

以一个OA系统来说,以下行为肯定是属于业务,应该放在领域层。

业务行为

  • 入职一个员工,录入员工基本信息
  • 更新员工的基本信息,名字,年龄等
  • 记录员工请假等信息,并根据这些计算员工当月薪资

系统行为

  • 日志记录,记录哪些管理员登录了OA系统,操作了什么
  • 缓存,为了加快查询速度,对一些信息做了缓存

上述是一些简单明了的行为,可以很快识别的,事实在,在领域驱动开发中,识别领域行为是比较重要的一个步骤。

在分析完成后,你就清楚,属于业务行为的逻辑,就应该放入领域层。而系统行为,你就可以放入应用层或其它恰当的非领域层。

领域层与具体的数据库无关

你需要忘记数据库这个东西,不管是关系型还是非关系型。

在编写领域层的代码时,最重要的一个点是先分析与建模业务行为,对具体的存储或其它行为进行抽象,至于存储具体是由关系型数据库来实现,还是非关系型数据库来实现,这是在领域层不应该关心的。

就拿myddd-vertx来说,虽然框架提供了BaseEntity基类,它在概念上与数据库有所关联,这是因为在实际项目中,数据库仍是主流。甚至这个基类中使用了诸如@Id,@Version等一些Annotation.

# BaseEntity源码
/**
* 基于ID的一个基类
*/
@MappedSuperclass
abstract class BaseEntity : Entity {

companion object {
private val idGenerator by lazy { InstanceFactory.getInstance(IDGenerator::class.java) }
}

@Id
var id:Long = 0

@Version
var version:Long = 0

override var created:Long = 0

override var updated:Long = 0

init {
this.id = idGenerator.nextId()
this.created = System.currentTimeMillis()
}

override fun getId(): Long {
return id
}
}

但就算是如此,这个基类仍然与具体的数据库框架无关联。依赖的是javax.persistence.*而非任何hibernate中的东西。甚至你可以为这个存储行为提供一个非数据库实现,比如内存等。

但这不是全部,虽然主流项目中都存在数据库,但这绝不意味着所有项目都和数据库相关联。再比如myddd-starter这个服务,它是代码生成的一个服务,很显然,在代码生成的业务中,不需要数据库,所以它的领域层甚至与存储都没有关系。

class Scaffold {

lateinit var name:String

lateinit var source: String

lateinit var version:String

companion object {
private val vertx by lazy { InstanceFactory.getInstance(Vertx::class.java) }
private val randomIdString by lazy { InstanceFactory.getInstance(RandomIDString::class.java) }
private val zipEngine by lazy { InstanceFactory.getInstance(ZipEngine::class.java) }
private var CACHE_DIR = System.getProperty("java.io.tmpdir") + "Scaffold" + File.separator

fun createScaffold(source:String,name:String,version:String = "1.0.0-SNAPSHOT"):Scaffold{
val scaffold = Scaffold()
scaffold.source = source
scaffold.name = name
scaffold.version = version
return scaffold
}
}


suspend fun generateProject(data:JsonObject):Future<String>{
return try {
//.... 省略具体实现代码
Future.succeededFuture(destZip)
}catch (t:Throwable){
Future.failedFuture(t)
}
}

}

上面的Scaffold就是核心领域类。它与数据库根本没有关系,它也只有一个核心方法就是generateProject,在这个过程中,还抽象了一些诸如解析freemarker,打包zip等接口。意味着在领域层,甚至没有具体的解析freemarker或打包成ZIP的代码。

它们都是抽象

//抽象的ZIP行为
interface ZipEngine {
suspend fun zipDirectory(source:String, name:String):Future<Unit>
}

还有解析器

//解析器抽象,有很多不同的解析行为,比如解析freemarker,解析特别的目录等
interface Resolver {
suspend fun accept(filePath:String):Future<Boolean>
suspend fun process(data:JsonObject,filePath: String):Future<List<ResolverFile>>
}

最重要的是:抽象

在领域层,最重要的一个概念就是抽象

对数据库的具体操作,抽象成Repostiroy

interface ProxyRepository: EntityRepository {
suspend fun syncEmployeeList(isvAuthCodeId:Long,employeeList:List<ProxyEmployee>):Future<Unit>
suspend fun syncOrganizationList(isvAuthCodeId:Long,organizationList:List<ProxyOrganization>):Future<Unit>
}

而对其它非数据库操作,比如解析freemarker,打包ZIP等,如同上面代码所示,也需要抽象出接口。

领域层的业务一定是依赖于抽象与接口,而非具体的实现

请特别铭记这一点,它是非常重要的。