Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a DDD repository be stateful?

I’m designing a shipping application and trying to use Clean Architecture. I’m trying to figure out where to hold state of a Shipment object so that I don’t have to re-instantiate a new object every time a user clicks on a button in the UI. Here is the flow.

  1. User enters a delivery number in the UI
  2. UI Controller handles the UI event and instantiates an instance of the Use Case Interactor a. A repository instance in passed into the constructor of the Use Case Interactor
  3. The Use Case interactor instantiates an instance of Shipment by calling a Factory(e.g. CREATE_BY_DELIVERY). The factory calls the Repository to collect the data from the database.
  4. Delivery data is populated on the UI
  5. User then clicks a Rate Quote button
  6. UI Controller handles the button click event and calls the RATE_QUOTE method of the Use Case Interactor a. Does the Use Case Interactor need to call the Shipment factory again as in step #3 or can the Use Case Interactor get an instance of the Shipment object that was already created in step #3?
  7. Rates are displayed on the UI
  8. User then clicks the PROCESS shipment button
  9. UI Controller handles the button click event and calls the PROCESS_SHIPMENT method of the Use Case Interactor a. Does the Use Case Interactor need to call the Shipment factory again as in step #3 or can the Use Case Interactor get an instance of the Shipment object that was already created in step #3?

Should the state of the shipment object be an instance variable on the UI Controller, Use Case Interactor or the Repository? Ideally, I'd like to save it somewhere so I don't need to keep creating a new object every time a user clicks a button on the UI.

Thank you in advance!

like image 565
Tony Raimo Avatar asked Jan 04 '23 01:01

Tony Raimo


2 Answers

Can a DDD repository be stateful?

Yes, absolutely -- that was part of the point in the original description

A REPOSITORY represents all objects of a certain type as a conceptual set (usually emulated). It acts like a collection, except with more elaborate querying capability.... For each type of object that needs global access, create an object that can provide the illusion of an in memory collection of all objects of that type.

In other words, the point is to separate the application component from the implementation details of the collection. As far as the app can tell, the repository can be implemented as a stateful, in memory, key/value store.

Ideally, I'd like to save it somewhere so I don't need to keep creating a new object every time a user clicks a button on the UI.

As a matter of keeping your code easy to understand, you should probably just create a new object every time, and deal with the complications of caching only when you have a clear business case for it.

That said, there's absolutely no reason that the implementation of the repository can't include a cache of recently used objects. You just have to be willing to invest in the cache invalidation strategy.

Remember, Phil Karlton taught us years ago

There are only two hard things in Computer Science: cache invalidation and naming things.

like image 163
VoiceOfUnreason Avatar answered Jan 05 '23 14:01

VoiceOfUnreason


Theoretically it is by definition always stateful. You are asking if a Repository can use a cache when loading/storing the Aggregates. So yes again, but. Caching is a complicated thing that should be avoided unless is necessary.

In this particularly case, using a cache hurts the horizontal scalability of the Application. It is hard to have multiple instances of it. It is hard to use optimistic locking at the database/persistence level, the Aggregate's version needs to be correctly set when loading an Aggregate (it must be the greatest) otherwise the expected version (the loaded version + 1) doesn't match. In other words, it is hard to invalidate the cache in an efficient way, as @VoiceOfUnreason said.

So, if you are willing to loose horizontal scalability then yes, you can cache the Aggregates.

P.S. this does not apply to Event sourced aggregate repositories. In that case, you could keep the snapshots in memory but still touch/query the Event store for the new events.

like image 20
Constantin Galbenu Avatar answered Jan 05 '23 14:01

Constantin Galbenu