Skip to main content

领域层

业务与非业务#

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

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

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

业务行为

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

系统行为

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

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

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

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

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

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

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

# BaseEntity源码/** * 基于ID的一个基类 */@MappedSuperclassabstract 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等,如同上面代码所示,也需要抽象出接口。

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

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