The problem I have relates to an existing system in a specific business domain. I've simplified it into a very similar 'Library/Book' relationship to articulate the problem. Answers such as 'why would you do it that way' are not helpful, as this is very similar to what I have, and need to solve for. Help that reflects that is very much appreciated.
I am using a no-sql database, which isn't particularly relevant to the question but the relationships cannot be modeled in SQL or use features like fk drop cascades.
Suppose I have a monolithic library service that I want to incrementally rewrite into a new service with newer libraries. Upgrade in place is not feasible. Part of this service that is tightly coupled includes:
- A table of Libraries
- A table of Books
- A table of LibraryBooks to represent the meta data of a book as it relates to a specific library (such as quantity, stock) with a partition key of library id and a clustering key of book id
- A table of LibrariesByBook that contains a denormalized Library entity with a partition key of book id and clustering key of library id
- When a Book, LibraryBook or Library is updated in any way, there's a hold list for that library that needs to be 'recalculated.' (this may not make sense, but this is close to what I have)
- When a LibraryBook is added (a book is added to a library) we need to verify that book and library exist.
- When a Library is updated, the LibrariesByBook table needs to be updated with the Library record.
The call graph ends up looking like this:
There aren't any cycles between method calls, however there is between the interfaces and the interfaces are very tightly coupled. For example, if we want to move the interfaces without decomposing a specific interface, all of the interfaces must be moved together.
Since the cycles are only on the read
method, having the read
interfaces separated would reduce the coupling and have tighter scoped interfaces with fewer collaborators. This means that the interfaces themselves are easier to understand for someone unfamiliar with the code, easier to test, easier to modify, etc.
However, breaking up entity/crud interfaces is very unusual. I've never actually seen any API or class hierarchy that does that, and it's not intuitive where the entity crud interfaces are. Breaking up the interfaces is going to be a hard sell to other developers, and some developers don't have problems with circular dependencies between interfaces.
Should the CRUD interfaces be broken up, leave the call cycles or is there another solution I'm not seeing?