Support “Unit of Work” pattern
Problem
Whenever performing operations on objects in your application which get directly persisted to the database can involve:
- slow performance when a lot of operations are made (lots of small database calls)
- complexity dealing with potential read inconsistencies during a long transaction
Solution
A solution to this problem is referred to as a “Unit of Work” which proposes to keep track of changes done within a transaction that can affect the database, and taking care of figuring out the changes which need to be done at the database level as a result.
Details
See RFC
See also internal notes
Hello team, it would be great if Prisma will have this feature, here is a use case:
- Given I have a quiz question entity
- And this question entity have a list of answer options
- When I add or remove some answer options from the question
- And create or update the question
- The question answer options should be updated
Right now developers have to track the changes by themselves like in the screenshot below (it is a JS code):

Update for my case
============================================================================
I updated the code to look like the screenshot below, I hope it can help:

This would be a nice feature however I think Prisma is far away from this, UOW is usually used in DDD and provided in big persistence frameworks in JAVA and C# such as Hibernate, JPA, and Entity Framework.
Prisma is meant for simplicity, and it obviously takes another way of doing things
@tareksalem Simplicity for who? Prisma users or Prisma developers that maintain the ORM? The feature will add a lot of simplicity to the end user, however it could be more work for the ORM developers.
We would also benefit from this at work.
I would love to see better support for DDD/"unit of work"-style applications as well! However, I would like offer a slightly different angle to this topic.
Unless these is some other way I haven't found yet, it seems really hard to properly execute domain logic on an "aggregate"(DDD term for a bunch of data that is always worked on together for internal consistency) with prisma.
to explain where I am coming from a bit better, what I usually want when I try to build clean stuff:
- fetch a root model with a few defined relations included (ie: the aggregate)
- pass the whole thing (maybe mapped somehow) to "the logic" (ideally totally separate from storage concerns and synchronous)
- take the resulting data and - and here comes the missing part - "apply" it to prisma
I generally see two options for this, either
- track all the changes (new stuff, changed fields, changes to relations, deleted stuff) - ie: traditional unit of work
- or, keep a copy of the fetched aggregate and "diff" the one you want to save, therefore working out all necessary changes that need to be made to the database
I understand that is not entirely the same, but to me some sort of "compare to original and apply changes" feature feels a lot more natural to add/build on top of prisma than an entire change-tracking solution.
So, after a few hours of playing with it, it is definitely possible to achieve the unit of work pattern. I'm using NestJS to implement it, taking some inspiration for how I've previously worked with .NET and Entity Framework.
Planning on writing a blog post about it and also publishing an example repo once I tidy some things up. Will report back here with more details when I have them. 🙂
In terms of the DDD side of it, I think it's important to decouple the concepts of domain models and database persistence. With tools like Entity Framework these lines can become blurred due to EF's powerful modelling and change tracking. Prisma doesn't work like this but it's still possible to create rich domain models (aggregate, entities, value objects) and map them to and from Prisma models in repositories. (I have written a short ebook on DDD if you're interested).
I was also looking for a solution to this and ended up developing something that I believe could be useful to someone. Here is the example repository
Back to this issue, the unit of work pattern could be implemented outside prisma easily, you could have multiple repositories wrap prisma operations like insert, delete, or update and you can build a wrapper class that puts those operations in a Prisma transaction, basically the unit of work is more than database integration because you can write those changed objects to a file, database or even on a network, so the unit of work should be implemented by yourself or a library/framework designed for domain driven design, from prisma perspective the unit of work pattern it has is the transaction mechanism
So, after a few hours of playing with it, it is definitely possible to achieve the unit of work pattern. I'm using NestJS to implement it, taking some inspiration for how I've previously worked with .NET and Entity Framework.
Planning on writing a blog post about it and also publishing an example repo once I tidy some things up. Will report back here with more details when I have them. 🙂
@mbrookson Any update on that blog post?
Even an example repository to see how your solution compares to @syllomex would be nice.
I think TS asks significantly more complex thing than just coordination of transactions between repositories about repeating of Entity Framework (or ex. mikro-orm) experience when you have mix of Identity Map, UnitOfWork and managed entities tracking changes when you can get efficient implicit transactions on calling em.flush().
I suggest check https://mikro-orm.io/docs/unit-of-work
Sorry, been rather busy and forgot about this. I never did get round to writing a blog post, but I started to throw together an example repo which contains a unit of work pattern with NestJS. It's far from finished, the tests don't work, but if you spin up a postgres db you can get it working locally and call the POST /users endpoint to see it in action. There are however a few considerations. I ended up trialling a pattern similar to this in my team at work but we ended up removing it and used a different (less ideal) approach. I'll explain why.
Here's the repo: https://github.com/mbrookson/nestjs-ddd
It contains:
- A
UnitOfWorkProviderimplementation which uses the NestJS CLS service package, which in turn uses the Node.js AsyncLocalStorage feature to store and retrieve data across an asynchronous call stack. It's a pretty amazing library! It creates an interactive Prisma transaction and stores it in CLS so it can be retrieved later on. https://github.com/mbrookson/nestjs-ddd/blob/main/src/infrastructure/unit-of-work.provider.ts - A
UseUnitOfWorkdecorator which can be used on any function to indicate the entry point for a unit of work. It invokes theUnitOfWorkProviderto create a new transaction. https://github.com/mbrookson/nestjs-ddd/blob/main/src/infrastructure/unit-of-work.decorator.ts - An example controller and example event handler which both use the unit of work decorator
- An example user repository which uses a
TransactionProviderto retrieve an active transaction. https://github.com/mbrookson/nestjs-ddd/blob/main/src/infrastructure/user.repository.ts
This repo also implements a transactional outbox pattern, which can be used to store domain events in a transaction and then consume them regularly and run their relevant handlers in separate units of work.
The downside to this approach and the reason we decided not to do this was that we ran into a few issues.
- The unit of work pattern is more familiar in platforms like .NET, where Entity Framework implements change tracking and transactions are created only at the point of saving data. As touched upon in the comment above, TS as a language doesn't really offer this ability as easily, and the tools and libraries in the ecosystem are certainly not as mature as anything in .NET for these kind of patterns.
- Related to the above, the issue with this implementation is that the Prisma transaction is actually opened in the database at the point where the unit of work begins. This means the transaction is open for the duration of the operation. This is not good! It means holding onto transactions much longer than necessary, and in a high traffic system this could cause deadlocks and connection exhaustion. Entity Framework doesn't have the same issue because it tracks changes to entity object and only opens and commits the transaction at the point where
SaveChangesis called. We ran into these long-lasting transaction issues at work and hence decided to not use this approach.
As a final note, at work we decided that we'd essentially consider each command handler as a unit of work. This means a command will read data and create an aggregate instance, do some domain logic, then perform any required changes in a Prisma transaction that we manually create within that command handler. It removed a lot of complexity that this example DDD repo introduces, stopped us having long-lasting transactions and resolve all issues we were having due to this.
It was a nice idea but sometimes keeping things simple and trying to embrace the tools you're using rather than forcing in concepts from previous experience can actually be a better path for success.
Hope this helps anyone who finds this. And I'm open to developing this more and working with anyone who wants to improve or expand on it.
@mbrookson Wow, thanks for the excellent write-up! You could either add this write-up as a README, or make it into the blog post itself! :)
I really appreciate the background, and admission that you're not using it due to the extra overhead and other issues.
In the name of expediency, I ended up with more of a Repository Factory pattern. It doesn't provide the UnitOfWork benefits (namely, the ability to .save() on multiple entities at once, like EntityFramework), but it does obfuscate my repositories further away from my business logic, and provides the ability to inject a single factory in order to get access to all my repositories.
It feels like ok design, mixed with -- as you noted -- keeping things simple.
Truncated example, for reference:
@Injectable()
export class RepositoryFactory {
private prismaService: PrismaService = new PrismaService();
userRepository: UserRepository = new UserRepository(this.prismaService);
otherRepository: OtherRepository = new OtherRepository(this.prismaService);
}
usage:
export class UserService {
private userRepository: UserRepository;
constructor(private readonly repositoryFactory: RepositoryFactory) {
this.userRepository = this.repositoryFactory.userRepository;
}
}
It somewhat breaks SOLID principles by instantiating PrismaService, but the RepositoryFactory itself gets injected everywhere else, and this creates the opporunity for me to quickly ferret out code smells from other engineers, as PrismaService should only ever be used within the RepositoryFactory.
Seems okay to me 👍 Why instantiate the PrismaService yourself and not also inject it? I believe it's recommended to only create one instance of the Prisma client in the lifetime of an application.
We're using CQRS and command handlers, but this is comparable to your service pattern. With our implementation we decided to have repositories which can return a Prisma operation. This means if we have multiple entities that needs updating we can call into multiple repositories and use a Prisma transaction in our command handler.
class UserCreatedCommandHandler implements ICommandHandler<UserCreatedEvent> {
constructor(
private readonly prisma: PrismaProvider,
private readonly userRepository: UserRepository,
private readonly otherRepository: OtherRepository
) {}
handle(event: UserCreatedEvent) {
const user = User.create({
// user properties....
});
const otherEntity = new Something();
await this.prisma.$transaction([
this.userRepository.create(user),
this.otherRepository.doSomething()
]);
}
}
class UserRepository {
constructor(private readonly prisma: PrismaProvider) {}
create(user: User) {
return this.prisma.user.create({
data: {
// map aggregate properties to db create...
}
})
}
}
Dumb example but hopefully demonstrates the point. Quite liking this solution as a half-way house. Still gives us a good separation of concerns, kind-of units of work and nice simple code to follow and maintain.
hi there any update for this issue in prisma since 2019? it is about orm problem that we can not executer multiple quries and just have a simple interface that will be implemented by prisma/
class UOW { userRepo:IUserRepository orderRepo: IOrderRepository startTransaction() commit() rollback() }
how can we implement this? the suggested answers are passing a function to uow.transaction(fn) but actually it's not what we want because the original uow interface doesn't accept function to be executed, in order to change prisma to raw sql (not using any orms) it some how doesn't make sence that to implement an interface which will take a function for transaction. so then what is benfit of having interface we are still coupling our business logic to prisma( just not using the name "prisma" but implicitly using its behavior).