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,如果一切顺利,领域层的单元测试也通过了。

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

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

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

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

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