| 
									
										
										
										
											2017-09-19 20:39:48 +05:30
										 |  |  | --- | 
					
						
							|  |  |  | layout: pattern | 
					
						
							|  |  |  | title: Unit Of Work | 
					
						
							|  |  |  | folder: unit-of-work | 
					
						
							|  |  |  | permalink: /patterns/unit-of-work/ | 
					
						
							| 
									
										
										
										
											2017-09-24 12:56:06 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-19 20:39:48 +05:30
										 |  |  | categories: Architectural | 
					
						
							|  |  |  | tags: | 
					
						
							| 
									
										
										
										
											2019-12-13 21:09:28 +02:00
										 |  |  |  - Data access | 
					
						
							| 
									
										
										
										
											2020-07-26 12:10:48 +03:00
										 |  |  |  - Performance | 
					
						
							| 
									
										
										
										
											2017-09-19 20:39:48 +05:30
										 |  |  | --- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Intent
 | 
					
						
							| 
									
										
										
										
											2020-08-18 20:07:47 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | When a business transaction is completed, all the the updates are sent as one big unit of work to be  | 
					
						
							|  |  |  | persisted in one go to minimize database round-trips.  | 
					
						
							| 
									
										
										
										
											2020-07-26 12:10:48 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | ## Explanation
 | 
					
						
							| 
									
										
										
										
											2020-08-18 20:07:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-26 12:10:48 +03:00
										 |  |  | Real world example | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-18 20:07:47 +03:00
										 |  |  | > We have a database containing student information. Administrators all over the country are 
 | 
					
						
							|  |  |  | > constantly updating this information and it causes high load on the database server. To make the 
 | 
					
						
							|  |  |  | > load more manageable we apply to Unit of Work pattern to send many small updates in batches.       
 | 
					
						
							| 
									
										
										
										
											2020-07-26 12:10:48 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | In plain words | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-18 20:07:47 +03:00
										 |  |  | > Unit of Work merges many small database updates in single batch to optimize the number of 
 | 
					
						
							|  |  |  | > round-trips. 
 | 
					
						
							| 
									
										
										
										
											2020-07-26 12:10:48 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | [MartinFowler.com](https://martinfowler.com/eaaCatalog/unitOfWork.html) says | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-18 20:07:47 +03:00
										 |  |  | > Maintains a list of objects affected by a business transaction and coordinates the writing out of 
 | 
					
						
							|  |  |  | > changes and the resolution of concurrency problems.
 | 
					
						
							| 
									
										
										
										
											2020-07-26 12:10:48 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | **Programmatic Example** | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Here's the `Student` entity that is being persisted to the database. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```java | 
					
						
							|  |  |  | public class Student { | 
					
						
							|  |  |  |   private final Integer id; | 
					
						
							|  |  |  |   private final String name; | 
					
						
							|  |  |  |   private final String address; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public Student(Integer id, String name, String address) { | 
					
						
							|  |  |  |     this.id = id; | 
					
						
							|  |  |  |     this.name = name; | 
					
						
							|  |  |  |     this.address = address; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public String getName() { | 
					
						
							|  |  |  |     return name; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public Integer getId() { | 
					
						
							|  |  |  |     return id; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public String getAddress() { | 
					
						
							|  |  |  |     return address; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-18 20:07:47 +03:00
										 |  |  | The essence of the implementation is the `StudentRepository` implementing the Unit of Work pattern.  | 
					
						
							|  |  |  | It maintains a map of database operations (`context`) that need to be done and when `commit` is  | 
					
						
							|  |  |  | called it applies them in single batch. | 
					
						
							| 
									
										
										
										
											2020-07-26 12:10:48 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```java | 
					
						
							|  |  |  | public interface IUnitOfWork<T> { | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |   String INSERT = "INSERT"; | 
					
						
							|  |  |  |   String DELETE = "DELETE"; | 
					
						
							|  |  |  |   String MODIFY = "MODIFY"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void registerNew(T entity); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void registerModified(T entity); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void registerDeleted(T entity); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void commit(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-13 13:19:21 +01:00
										 |  |  | @Slf4j | 
					
						
							| 
									
										
										
										
											2020-07-26 12:10:48 +03:00
										 |  |  | public class StudentRepository implements IUnitOfWork<Student> { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-30 20:28:47 +03:00
										 |  |  |   private final Map<String, List<Student>> context; | 
					
						
							|  |  |  |   private final StudentDatabase studentDatabase; | 
					
						
							| 
									
										
										
										
											2020-07-26 12:10:48 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |   public StudentRepository(Map<String, List<Student>> context, StudentDatabase studentDatabase) { | 
					
						
							|  |  |  |     this.context = context; | 
					
						
							|  |  |  |     this.studentDatabase = studentDatabase; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @Override | 
					
						
							|  |  |  |   public void registerNew(Student student) { | 
					
						
							|  |  |  |     LOGGER.info("Registering {} for insert in context.", student.getName()); | 
					
						
							|  |  |  |     register(student, IUnitOfWork.INSERT); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @Override | 
					
						
							|  |  |  |   public void registerModified(Student student) { | 
					
						
							|  |  |  |     LOGGER.info("Registering {} for modify in context.", student.getName()); | 
					
						
							|  |  |  |     register(student, IUnitOfWork.MODIFY); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @Override | 
					
						
							|  |  |  |   public void registerDeleted(Student student) { | 
					
						
							|  |  |  |     LOGGER.info("Registering {} for delete in context.", student.getName()); | 
					
						
							|  |  |  |     register(student, IUnitOfWork.DELETE); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private void register(Student student, String operation) { | 
					
						
							|  |  |  |     var studentsToOperate = context.get(operation); | 
					
						
							|  |  |  |     if (studentsToOperate == null) { | 
					
						
							|  |  |  |       studentsToOperate = new ArrayList<>(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     studentsToOperate.add(student); | 
					
						
							|  |  |  |     context.put(operation, studentsToOperate); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @Override | 
					
						
							|  |  |  |   public void commit() { | 
					
						
							|  |  |  |     if (context == null || context.size() == 0) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     LOGGER.info("Commit started"); | 
					
						
							|  |  |  |     if (context.containsKey(IUnitOfWork.INSERT)) { | 
					
						
							|  |  |  |       commitInsert(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (context.containsKey(IUnitOfWork.MODIFY)) { | 
					
						
							|  |  |  |       commitModify(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (context.containsKey(IUnitOfWork.DELETE)) { | 
					
						
							|  |  |  |       commitDelete(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     LOGGER.info("Commit finished."); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private void commitInsert() { | 
					
						
							|  |  |  |     var studentsToBeInserted = context.get(IUnitOfWork.INSERT); | 
					
						
							|  |  |  |     for (var student : studentsToBeInserted) { | 
					
						
							|  |  |  |       LOGGER.info("Saving {} to database.", student.getName()); | 
					
						
							|  |  |  |       studentDatabase.insert(student); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private void commitModify() { | 
					
						
							|  |  |  |     var modifiedStudents = context.get(IUnitOfWork.MODIFY); | 
					
						
							|  |  |  |     for (var student : modifiedStudents) { | 
					
						
							|  |  |  |       LOGGER.info("Modifying {} to database.", student.getName()); | 
					
						
							|  |  |  |       studentDatabase.modify(student); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private void commitDelete() { | 
					
						
							|  |  |  |     var deletedStudents = context.get(IUnitOfWork.DELETE); | 
					
						
							|  |  |  |     for (var student : deletedStudents) { | 
					
						
							|  |  |  |       LOGGER.info("Deleting {} to database.", student.getName()); | 
					
						
							|  |  |  |       studentDatabase.delete(student); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-18 20:07:47 +03:00
										 |  |  | Finally, here's how we use the `StudentRepository` and `commit` the transaction. | 
					
						
							| 
									
										
										
										
											2020-07-26 12:10:48 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```java | 
					
						
							|  |  |  |     studentRepository.registerNew(ram); | 
					
						
							|  |  |  |     studentRepository.registerModified(shyam); | 
					
						
							|  |  |  |     studentRepository.registerDeleted(gopi); | 
					
						
							|  |  |  |     studentRepository.commit(); | 
					
						
							|  |  |  | ``` | 
					
						
							| 
									
										
										
										
											2017-09-19 20:39:48 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-07 20:01:13 +02:00
										 |  |  | ## Class diagram
 | 
					
						
							| 
									
										
										
										
											2020-08-18 20:07:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-19 20:39:48 +05:30
										 |  |  |  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Applicability
 | 
					
						
							| 
									
										
										
										
											2020-08-18 20:07:47 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-19 20:39:48 +05:30
										 |  |  | Use the Unit Of Work pattern when | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-26 23:25:36 +05:30
										 |  |  | * To optimize the time taken for database transactions. | 
					
						
							| 
									
										
										
										
											2017-09-25 21:23:12 +05:30
										 |  |  | * To send changes to database as a unit of work which ensures atomicity of the transaction. | 
					
						
							| 
									
										
										
										
											2017-09-27 00:04:06 +05:30
										 |  |  | * To reduce number of database calls. | 
					
						
							| 
									
										
										
										
											2017-09-19 20:39:48 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-26 12:10:48 +03:00
										 |  |  | ## Tutorials
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | * [Repository and Unit of Work Pattern](https://www.programmingwithwolfgang.com/repository-and-unit-of-work-pattern/) | 
					
						
							|  |  |  | * [Unit of Work - a Design Pattern](https://mono.software/2017/01/13/unit-of-work-a-design-pattern/) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-19 20:39:48 +05:30
										 |  |  | ## Credits
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | * [Design Pattern - Unit Of Work Pattern](https://www.codeproject.com/Articles/581487/Unit-of-Work-Design-Pattern) | 
					
						
							|  |  |  | * [Unit Of Work](https://martinfowler.com/eaaCatalog/unitOfWork.html) | 
					
						
							| 
									
										
										
										
											2020-07-07 21:19:14 +03:00
										 |  |  | * [Patterns of Enterprise Application Architecture](https://www.amazon.com/gp/product/0321127420/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321127420&linkCode=as2&tag=javadesignpat-20&linkId=d9f7d37b032ca6e96253562d075fcc4a) |