mardi 19 octobre 2021

Correct class hierarchy

I was wondering what is the correct way to organize my class hierarchy in the following situation.
I wanted to build an abstraction around postgresql advisory lock.

Note just for context: An advisory lock is a lock that you can obtain at a session or transaction level. Postgres handle all the complexity for you.

The code that I've written so far is something like

interface DBLockService

interface SessionLockService : DBLockService {
    fun acquire(id: Long)
    fun unlock(id: Long): Boolean
}


interface TransactionalLockService : DBLockService {
    fun txAcquire(id: Long)
}

abstract class BaseDBLockService(protected val entityManager: EntityManager): DBLockService {

    protected fun executeAcquire(preparedStatement: String, id: Long) {
        executeAcquire<Any>(preparedStatement, id)
    }

    protected inline fun <reified T> executeAcquire(preparedStatement: String, id: Long) =
        entityManager
            .createNativeQuery(preparedStatement, T::class.java)
            .setParameter("id", id)
            .singleResult as T
}

@Component
class LockServiceImpl(
    entityManager: EntityManager
) : BaseDBLockService(entityManager),
    SessionLockService {
    companion object {
        const val acquireStatement = "SELECT pg_advisory_lock(:id)"
        const val unlockStatement = "SELECT pg_advisory_unlock(:id)"
    }

    override fun acquire(id: Long) {
        executeAcquire(acquireStatement, id)
    }

    override fun unlock(id: Long) =
        executeAcquire<Boolean>(unlockStatement, id)

}

@Component
class TransactionalLockServiceImpl(
    entityManager: EntityManager
) : BaseDBLockService(entityManager),
    TransactionalLockService {
// very similar implementation
}

Looking at this code there is something that tell's me that there is something wrong:

  • DBLockService is a bit useless interface, there is no method
  • Are SessionLockService and TransactionalLockService just an implementation detail? Is it correct that there is a different interface for every "type" of lock?

But at the same time, if I remove the DBLockService seems very odd to me that there are 2 interfaces (SessionLockService and TransactionalLockService) with very similar context that are not related in any way.
Moreover, removing DBLockService, I'll have the 2 implementations (LockServiceImpl and TransactionalLockServiceImpl) that extends from the abstract class BaseDBLockService to implement these 2 interfaces but at the same time the abstract class is not related to them.

What to you think?
Thanks

Aucun commentaire:

Enregistrer un commentaire