Comments (5)
Here is my implementation:
- unit-of-work-orm.ts - base class to work with TypeOrm Repositories
- unit-of-work.ts - this is a class to convert TypeOrm repositories to Domain Repositories
- create-user.service.ts - here you get the repository that you need and then wrap an execution in a
this.unitOfWork.execute
Edit: UnitOfWork refactored to support correlationId
. This is needed to use different query runners for each request, otherwise query fails if there is a lot of concurrent requests since before they all used the same connection pool.
from domain-driven-hexagon.
There can be multiple ways of implementing it depending on your database sdk/orm.
I'd try to do it as generic as possible to be able to reuse it:
interface IUnitOfWork {
add<T extends Entity>(entities: T[]): Promise<void>
commit(): Promise<void>
abort(): Promise<void>
}
With add()
method you can add any amount of entities at any point, put them somewhere in an array internally, and then when you call commit()
it just saves everything at once.
Edit: sorry, the solution above is based on my other project which uses Event Sourcing and there is no need to include reads in the transaction there so it will not work for you I guess :)
if you want to include reads in the transaction, returning repositories seems like a good solution
from domain-driven-hexagon.
This is a job for Unit of Work pattern.
You can create a UnitOfWork class that accepts both repositories for member and payment, it will look something like this (if you are using typeorm):
export class MemberPaymentUnitOfWork {
@Transaction({ isolation: "SERIALIZABLE" })
save(
member: Member,
payment: Payment,
@TransactionRepository(Member)
memberRepository: Repository<Member>,
@TransactionRepository(Payment)
paymentRepository: Repository<Payment>
) {
await memberRepository.save(member);
await paymentRepository.save(payment);
}
}
or using a transaction manager you can create a generic UnitOfWork:
export class UnitOfWork {
@Transaction({ isolation: "SERIALIZABLE" })
save<T extends Entity>(
entities: T[],
@TransactionManager()
manager: EntityManager
) {
await manager.save(entities);
}
}
Read more about transaction decorators: Typeorm Transactions
from domain-driven-hexagon.
Thanks a lot for taking the time to answer quickly. I still haven't chosen the database and the ORM for my app, and i don't like how TypeORM makes the code highly coupled to it's decorators, do you think doing something like this is a good idea:
interface IUnitOfWork {
start(): Promise<{ paymentRepo: IPaymentRepo, memberRepo: IMemberRepo }>
commit(): Promise<void>
abort(): Promise<void>
}
the method start of IUnitOfWork, returns repositories where any writes/reads preformed by them is part of a transaction, and the transaction is only committed by calling commit, this should be pretty easy and straightforward to implement at the infrastructure layer and flexible enough to be reused for both domain events and regular use cases, this would mean that the use cases would not need to even know about the IUnitOfWork, they would just be passed the repositories returned by the start method.
Do you think this is a good idea ?
from domain-driven-hexagon.
If anyone else is having the same problem, i went with a more generic implementation of IUnitOfWork
, one that is shared by all subdomains in my monolith:
type Repos = {
booking: BookingRepositories,
notification: NotificationRepositories
}
interface IUnitOfWork {
start (): Promise<any>
abort (): Promise<void>
commit (): Promise<void>
}
However each subdomain must define and only use it's own IUnitOfWork
for example in the Booking
subdomain we have IBookingUnitOfWork
which extends IUnitOfWork
and overrides the start
method so it only returns { bookingRepositories: BookingRepositories }
type Repos = { booking: BookingRespoitories }
interface IBookingUnitOfWork extends IUnitOfWork {
start (): Promise<Repos>
}
it's important that subdomains only have access to their own repos and not the repos of other subdomains, the infrastructure will have only one implementation of IUnitOfWork
which will work on anything that extends it.
the two main flaws with this is that it is a bit complicated and it creates coupling between IUnitOfWork
and anything else that extends it which may or may not be a big issue depending on what you're working on this is a good enough compromise for me because my app has 4 subdomains and is a monolith with one event dispatcher.
from domain-driven-hexagon.
Related Issues (20)
- `StructuredClone()` function converts entity object into plain object, causing `undefined` properties when calling getters HOT 2
- @slonik/migrator is not compatible with slonik 30+ HOT 9
- InternalServerErrorException has incorrect documentation HOT 1
- FindUsersQuery should map data to response in order to avoid leaks HOT 1
- Why are ports defined in infrastructure layer? HOT 2
- Intermittent test failure for create-user HOT 7
- Clarification on the Practical Usage of Domain Services in DDD
- typeof id is undefined HOT 4
- execute start:dev throws error HOT 6
- Implement and add examples for Adapters and Providers from Infrastructure folder? HOT 6
- Enable GitHub discussions ? HOT 1
- Correct approach for different types of concept? HOT 1
- UnitOfWork creating many QueryRunners and not release them HOT 4
- Types are incorrect for value object's unpack method
- Dependency Inversion in new version HOT 1
- Queries using repositories HOT 1
- Query handler breaking the dependency rule? HOT 4
- Authentication module HOT 1
- Resolver returning type HOT 1
- resolver return HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from domain-driven-hexagon.