Skip to main content

基础设施层编码

1. 编写接口的实现

现在,我们需要为ISVClientRepository在基础设施层中提供一个实现


class ISVClientRepositoryHibernate : EntityRepositoryHibernate(),ISVClientRepository {

override suspend fun querySuiteTicket(suiteId: String, clientType: ISVClientType): Future<ISVSuiteTicket?> {
return singleQuery(
clazz = ISVSuiteTicket::class.java,
sql = "from ISVSuiteTicket where suiteId = :suiteId and clientType = :clientType",
params = mapOf("suiteId" to suiteId,"clientType" to clientType)
)
}

//...省略其它
}

我们定义了ISVClientRepositoryHibernate实现类,它是实现ISVClientRepository接口的。而它同时又继承了EntityRepositoryHibernate父类

tip

EntityRepositoryHibernate是对EntityRepository接口的实现。

关于仓储,上面这个代码只是演示了最简单的用法,先了解这一步就可以了,EntityRepositoryHibernate这个父类中提供了一些常见的操作。大部分情况下你使用它们就可以了,比如上面的使用singleQuery查询方法

2. 单元测试

现在我们可以开始针对EntityRepositoryHibernate实现编写一个单元测试。与前面领域层的单元测试不同,这个单元测试我们并不缺少实现,所以它是可以通过的


class ISVClientRepositoryTest : AbstractTest() {

private val clientRepository by lazy { InstanceFactory.getInstance(ISVClientRepository::class.java) }

@Test
fun testQuerySuiteTicket(vertx: Vertx,testContext: VertxTestContext){
GlobalScope.launch(vertx.dispatcher()) {
try {
val randomISVSuiteTicket = randomSuiteTicket()
val created = clientRepository.save(randomISVSuiteTicket).await()
testContext.verify {
Assertions.assertNotNull(created)
Assertions.assertTrue(created.id > 0)
}

val query = clientRepository.querySuiteTicket(suiteId = created.suiteId,clientType = ISVClientType.WorkPlusISV).await()
testContext.verify {
Assertions.assertNotNull(query)
}
}catch (t:Throwable){
testContext.failNow(t)
}
testContext.completeNow()
}
}

private fun randomSuiteTicket(): ISVSuiteTicket {
val suiteTicket = ISVSuiteTicket()
suiteTicket.suiteId = randomString()
suiteTicket.clientType = ISVClientType.WorkPlusISV
suiteTicket.suiteTicket = randomString()
return suiteTicket
}


}

可以仍然使用InstanceFactory来获取接口的实现

private val clientRepository by lazy { InstanceFactory.getInstance(ISVClientRepository::class.java) }

那它是怎么来的?

所有实例都是在AbstractTest中提供的


@ExtendWith(VertxExtension::class)
abstract class AbstractTest {

init {
InstanceFactory.setInstanceProvider(GuiceInstanceProvider(Guice.createInjector(object : AbstractModule(){
override fun configure() {
bind(Mutiny.SessionFactory::class.java).toInstance(
Persistence.createEntityManagerFactory("default")
.unwrap(Mutiny.SessionFactory::class.java))


bind(RandomIDString::class.java).to(RandomIDStringProvider::class.java)
bind(IDGenerator::class.java).toInstance(SnowflakeDistributeId())

bind(ISVClientRepository::class.java).to(ISVClientRepositoryHibernate::class.java)
}
})))
}
}

实现是在这里被注入进去的,然后通过InstanceFactory又获取到。

如无意外,上面你编写的这个单元测试是应该可以通过的。如果没有通过,说明你的代码或逻辑是有问题的,这就是TDD的作用。

TDD的一个重要作用是将复杂的业务拆成极小的代码块,你可以针对每一个小的代码块进行测试,确保它们正确,而不是等一个复杂业务编写完成,再做集成测试

这是保证高效编码的最有效的方式,还有更快的方式么?

3. 让领域层的单元测试通过

我们已经添加了ISVClientRepository的实现,现在是时候让我们的领域层刚刚编码的单元测试也通过了。

@ExtendWith(VertxExtension::class)
abstract class AbstractTest {

companion object {

val logger by lazy { LoggerFactory.getLogger(AbstractTest::class.java) }

val randomIDString by lazy { InstanceFactory.getInstance(RandomIDString::class.java) }

@BeforeAll
@JvmStatic
fun beforeAll(vertx: Vertx,testContext: VertxTestContext){
InstanceFactory.setInstanceProvider(GuiceInstanceProvider(Guice.createInjector(object : AbstractModule(){
override fun configure() {
bind(Mutiny.SessionFactory::class.java).toInstance(
Persistence.createEntityManagerFactory("default")
.unwrap(Mutiny.SessionFactory::class.java))

bind(ISVClientRepository::class.java).to(ISVClientRepositoryHibernate::class.java)
bind(RandomIDString::class.java).to(RandomIDStringProvider::class.java)
bind(IDGenerator::class.java).toInstance(SnowflakeDistributeId())

}
})))
testContext.completeNow()
}

}
}

完善领域层单元测试中的AbstractTest类,将接口的实现注入进去。

OK,如果一切顺利,领域层的单元测试也通过了。

你看,到此为止,你已经完成了一个领域业务的编码。

几乎所有领域业务的编码都是这么个过程,领域层,基础设施层,单元测试,在这些概念中循环往复。

而完成这个过程后,你就会发现:

几乎所有业务都在领域层,领域层才是最有价值的一部分

这就是领域驱动设计理念的魅力。