In Martin Fowler's book I read about UnitOfWork and IdentityMap patterns.
Author mentioned that it is a good idea to put identityMap inside the UnitOfWork. But how to do it ?
As far I understood IdentityMap is bounded by session but author doesn't mentioned it about UnitOfWork
Is
UnitOfWorkinstance bounded by session?Let's say we have client and order entities.
public clas Client{ List<Order> orders; ... }
and we got request to update client information(phone numeber) and add new order:
How many unitOfWork instances do wee need here? for each session? should we have separated instances for client and for order ?
How many IdentityMap instances do we need here ? for each instance ? should we have separated instances for client and for order ?
How many IdentityMap instances do we need for each unitOfWork instance ?
What if we got 2 concurrent requests ?
In Chapter 11 of Martin's book in question you read:
So, the
UnitOfWorkdoes not need to be bound to the session. The extent of the existence of theUnitOfWorkinstance depends on your design. In Martin's example in that same book, we can see that the UnitOfOWork instance is created per HTTP request (which I believe is the most classical use).In the JPA specification, the unit of work can be of two different natures: transaction-scoped persistence context, and extended-persistence context, but one can also handle it manually and get an application-managed unit of work (see this other answer). Which one you use depends on the use case.
Martin Fowler also answers that question in the book. In his section on
IdentityMapthere's an entire section about it.The book reads:
Where you could understand the session as the
UnitOfWorkin this case. Later in the book he explains:A few paragraphs later Martin explains where to put the
IdentityMap:So, there you have it, if your
UnitOfWorkis bound to a request, then you will have one or moreIdentityMapsin that instance of yourUnitOfWork.Yes, it is.
Now the extent of the "business transaction" could be short-lived, e.g. bound to the life of the HTTP request as in Martin Fowler's example above where the UnitOfWork seems to be bound to a thread-local variable per request.
Or the "business transaction" could encompass multiple requests, e.g. in JPA there's a concept of an extended persistence context, where the persistent context in JPA corresponds to a
UnitOfWorkpattern. In an extended persistence context, the business transaction extends for a longer period of time. A classical example of this is a shopping cart: when the user starts putting items in her shopping cart a context/unit of work is created, and the context won't get cleared until the user checks out her shopping cart (committing the changes) or her session expires (discarding everything in the unit of work).Now, there is also the idea of application-managed context, which basically means, start your unit of work when you think it's appropriate and close it when you no longer need it. For example, suppose you have a desktop application and a small database with isolated concurrency (i.e. every user only manages his own data and never conflict with other user's data). Suppose that the user's data could perfectly fit in the computer's memory. In a case like that, you could start your
UnitOfWorkat the start of the application and use it as a sort of cache for your database. When appropriate, you can flush the UnitOfWork to disk, e.g. when the user clicks a save button, but still keep it alive. When the user closes the application, the UnitOfWork is discarded.So, you can see there is a lot of nuances on what a "business transaction" actually means, or for how long the
UnitOfWorkshould exist.Multiple Units of Work
Based on the explanation so far, you can have multiple units of work for, among others, the following reasons:
UnitWorkper request, and if your application handles concurrent requests, then you would have multiple units of work instances at the same time.UnitOfWorkper extended transaction, and so, tied to the user's session. If you have multiple users, you can have multiple units of work.However, besides these, I have found other reasons why you may want to spawn a new unit of work during the same "business transaction".
It is not unusual that while you're executing a business transaction you may want to execute another totally independent one. For example, suppose that in order to place an order for the customer, the customer record must exist in the database, but if the customer record fails to be created (e.g. perhaps because another customer has same conflicting email), you still want to place the order in pending review status.
So, the problem you face here is that if you attempt to create the customer during the business transaction of placing an order and creating the customer fails, that contaminates your order transaction and your unit of work is forced to roll back everything. In a situation like this you may want to spawn a new unit of work and therefore a new, separate database transaction, to create the customer and if that separate unit of work fails to create the customer, this does not contaminate your order creation unit of work, allowing you to take measures to still persist your order without a customer in a pending review status.
I believe JPA's concept of a "context" is a very good example of how to define a unit of work. I would suggest that you study their specification particularly their section about the
EntityManager.