0

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: enter image description here

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?

3
  • Why does the library or the LibraryBook needs to change if the book changes? If you made a typo in the book title and correct that, the amount of books hold by that library doesn#t change. The state of those books (checked out, checked in) etc. doesn't change. The LibraryByBooks index doesn't change. Unless you allow the Id of the book to change, which would be a bad idea for so many reasons.
    – Polygnome
    Oct 6, 2020 at 7:12
  • Like I said in the example, the reasoning may not make complete sense. In my real situation, the waitlist would be analogous to a dynamic calculation that takes into account the contents of the actual book, and the contents of the actual book can change.
    – Nathanial
    Oct 6, 2020 at 14:53
  • "and the contents of the actual book can change" You have strange books. Last time I checked, none of the books I posess could change.
    – Polygnome
    Oct 6, 2020 at 16:49

3 Answers 3

1

How About...

createLibraryBook and deleteLibraryBook are on the wrong entity. I doubt that a LibraryBook should be responsible for such activities (beyond filling out its own properties). Library feels like a better scoping for such actions.

LibrariesByBook is an index, is it possible to push that down into the data layer as one, it could probably be over the LibraryBook backing store as it knows about libraries, and books. Surfacing the lookup methods on the Book (or if it must LibraryBook) to list Libraries?

1

You should have a repository object for you database and not expose librarybook and booklibrary at all. They are not enties just relationships.

repo.UpdateBook(book)

Can then do all the nessesary work and maintain all the relationships and calculated waitlists etc

1

I think the problem is a lack of top-down "ownership". Logically a Library references a number of LibraryBooks. A LibraryBook references one book.

So if you want to add a book to a library, you tell Library to add a book. It creates a LibraryBook, which adds a reference to the Book. If you want to edit a book, again tell the Library which book to edit, so it all ends up being done top-down.

LibrariesByBook is a bit of an oddity. It could be maintained by the Libraries, in cooperation with each other. Or create another "AllLibraries" above Library.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.