Code Monkey home page Code Monkey logo

Comments (5)

Sairyss avatar Sairyss commented on May 17, 2024 2

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.

Sairyss avatar Sairyss commented on May 17, 2024 1

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.

Sairyss avatar Sairyss commented on May 17, 2024

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.

MSE99 avatar MSE99 commented on May 17, 2024

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.

MSE99 avatar MSE99 commented on May 17, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.