diff --git a/unit-of-work/README.md b/unit-of-work/README.md index 56c1a6b77..617ddcd3c 100644 --- a/unit-of-work/README.md +++ b/unit-of-work/README.md @@ -1,37 +1,36 @@ --- title: Unit Of Work -category: Architectural +category: Data access language: en tag: - - Data access - - Performance + - Data access + - Decoupling + - Persistence + - Transactions --- ## Intent -When a business transaction is completed, all the updates are sent as one big unit of work to be -persisted in one go to minimize database round-trips. +The Unit of Work pattern maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems. ## Explanation Real-world example -> Arms dealer has a database containing weapon information. Merchants all over the town are -> constantly updating this information and it causes a 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. +> Imagine a library where a librarian tracks all the books that are borrowed and returned throughout the day. Instead of updating the library's inventory system every time a single transaction occurs, the librarian keeps a list of all the changes and updates the system once at the end of the day. This approach ensures that all changes are processed together, maintaining the integrity of the inventory and reducing the number of individual updates needed. This is analogous to the Unit of Work pattern in software, where all changes to a set of objects are tracked and committed as a single transaction to maintain consistency and efficiency. In plain words -> Unit of Work merges many small database updates in a single batch to optimize the number of -> round-trips. +> The Unit of Work pattern tracks changes to objects during a transaction and commits all changes as a single unit to ensure consistency and efficiency. [MartinFowler.com](https://martinfowler.com/eaaCatalog/unitOfWork.html) says -> Maintains a list of objects affected by a business transaction and coordinates the writing out of -> changes and the resolution of concurrency problems. +> Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems. **Programmatic Example** +Arms dealer has a database containing weapon information. Merchants all over the town are constantly updating this information causing a 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. + Here's the `Weapon` entity that is being persisted in the database. ```java @@ -43,9 +42,7 @@ public class Weapon { } ``` -The essence of the implementation is the `ArmsDealer` 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 a single batch. +The essence of the implementation is the `ArmsDealer` 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 a single batch. ```java public interface IUnitOfWork { @@ -103,7 +100,7 @@ public class ArmsDealer implements IUnitOfWork { */ @Override public void commit() { - if (context == null || context.size() == 0) { + if (context == null || context.isEmpty()) { return; } LOGGER.info("Commit started"); @@ -179,23 +176,48 @@ Here is the console output. ## Class diagram -![alt text](./etc/unit-of-work.urm.png "unit-of-work") +![Unit of Work](./etc/unit-of-work.urm.png "Unit of Work") ## Applicability -Use the Unit Of Work pattern when - -* To optimize the time taken for database transactions. -* To send changes to database as a unit of work which ensures atomicity of the transaction. -* To reduce the number of database calls. +* Use when you need to manage multiple operations that need to be treated as a single transaction. +* Ideal in scenarios where changes to the business objects must be tracked and saved in a coordinated manner. +* Useful when working with object-relational mapping (ORM) frameworks in Java such as Hibernate. ## 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/) +* [Repository and Unit of Work Pattern - Wolfgang Ofner](https://www.programmingwithwolfgang.com/repository-and-unit-of-work-pattern/) +* [Unit of Work - a Design Pattern - Mono](https://mono.software/2017/01/13/unit-of-work-a-design-pattern/) + +## Known Uses + +* Implementations in Java-based ORM frameworks like Hibernate. +* Enterprise applications where multiple database operations need to be atomic. +* Complex transactional systems where multiple objects are modified and persisted together. + +## Consequences + +Benefits: + +* Ensures data integrity by managing transactions effectively. +* Reduces the number of database calls by batching them together. +* Simplifies the persistence logic by decoupling transaction management from the business logic. + +Trade-offs: + +* Can introduce complexity in managing the life cycle of objects within the unit of work. +* Potential performance overhead if not managed properly, especially with large datasets. + +## Related Patterns + +* [Identity Map](https://java-design-patterns.com/patterns/identity-map/): Helps to ensure that each object is only loaded once per transaction, reducing redundancy and improving performance. +* [Repository](https://java-design-patterns.com/patterns/repository/): Often used in conjunction with Unit of Work to abstract the persistence logic and provide a cleaner way to access data. +* [Transaction Script](https://java-design-patterns.com/patterns/transaction-script/): While different in its procedural approach, it can complement Unit of Work by managing transactional logic at a higher level. ## 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) -* [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) +* [Domain-Driven Design: Tackling Complexity in the Heart of Software](https://amzn.to/3wlDrze) +* [Java Persistence with Hibernate](https://amzn.to/44tP1ox) +* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR) +* [Unit Of Work Design Pattern - Code Project](https://www.codeproject.com/Articles/581487/Unit-of-Work-Design-Pattern) +* [Unit Of Work - Martin Fowler](https://martinfowler.com/eaaCatalog/unitOfWork.html) diff --git a/unit-of-work/src/main/java/com/iluwatar/unitofwork/App.java b/unit-of-work/src/main/java/com/iluwatar/unitofwork/App.java index 6ed0efd85..ca1bf66db 100644 --- a/unit-of-work/src/main/java/com/iluwatar/unitofwork/App.java +++ b/unit-of-work/src/main/java/com/iluwatar/unitofwork/App.java @@ -44,7 +44,7 @@ public class App { var silverTrident = new Weapon(3, "silver trident"); // create repository - var weaponRepository = new ArmsDealer(new HashMap>(), + var weaponRepository = new ArmsDealer(new HashMap<>(), new WeaponDatabase()); // perform operations on the weapons diff --git a/unit-of-work/src/main/java/com/iluwatar/unitofwork/ArmsDealer.java b/unit-of-work/src/main/java/com/iluwatar/unitofwork/ArmsDealer.java index 9c77c13a1..9d10cce7d 100644 --- a/unit-of-work/src/main/java/com/iluwatar/unitofwork/ArmsDealer.java +++ b/unit-of-work/src/main/java/com/iluwatar/unitofwork/ArmsDealer.java @@ -73,7 +73,7 @@ public class ArmsDealer implements UnitOfWork { */ @Override public void commit() { - if (context == null || context.size() == 0) { + if (context == null || context.isEmpty()) { return; } LOGGER.info("Commit started"); diff --git a/unit-of-work/src/test/java/com/iluwatar/unitofwork/ArmsDealerTest.java b/unit-of-work/src/test/java/com/iluwatar/unitofwork/ArmsDealerTest.java index c870a1f31..b4d93d99d 100644 --- a/unit-of-work/src/test/java/com/iluwatar/unitofwork/ArmsDealerTest.java +++ b/unit-of-work/src/test/java/com/iluwatar/unitofwork/ArmsDealerTest.java @@ -46,7 +46,7 @@ class ArmsDealerTest { private final Map> context = new HashMap<>(); private final WeaponDatabase weaponDatabase = mock(WeaponDatabase.class); - private final ArmsDealer armsDealer = new ArmsDealer(context, weaponDatabase);; + private final ArmsDealer armsDealer = new ArmsDealer(context, weaponDatabase); @Test void shouldSaveNewStudentWithoutWritingToDb() {