Keri sisuni

Unit of Work

Unit of Work on disainimuster, mille abil saame sarnaselt andmebaasi transaktsioonile moodustada mitmest toimingust ühe - kõik ühikusse kuuluvad toimingud kas õnnestuvad või ebaõnnestuvad. Sellised toimingud andmetega on ärirakendustes igapäevased.

Vaatleme näiteks arve lisamise toimingut. Arvel peab olema vähemalt üks rida, sest muidu pole arve korrektne. Andmebaasi salvestatakse arve sellisel juhul kahe käsuna:

  1. Sisesta uus arve ning küsi selle ID
  2. Sisesta uus arverida
  3. Seo arvereaga maksud

Kui teine või kolmas käsk ebaõnnestuksid, siis oleks meil andmebaasis ebakorrektne arve.

Selles peatükis vaatleme Unit of Work implementatsioone, mis suhtlevad ainult andmebaasidega. Praktikas võib Unit of Work sisaldada ka komponente, mis suhtlevad väliste süsteemide või seadmetega, kus toimingute terviklikkus pole alati garanteeritud.

Unit of Work klass

Lihtsuse huvides loome UoW jaoks klassi, kuhu antakse kaasa repositoryd. Selliselt on meil lihtsam kõikm repositoryd korraga teenustesse ajada, kus neid vaja läheb. Lisaks defineerime UoW-le kolm uut meetodi:

  • BeginTransaction() - alustame andmebaasi transaktsiooniga
  • Commit() - jõusta andmebaasi transaktsioon
  • Rollback() - tühista andmebaasi transaktsioon

UoW klassi loome järgnevalt.

public class UnitOfWork
{
    public IInvoiceRepository InvoiceRepository { get; private set; }
    public IUserRepository UserRepository { get; private set; }

    private readonly ApplicationDbContext _dbContext;

    public UnitOfWork(IInvoiceRepository invoiceRepository,
                      IUserRepository userRepository,
                      ApplicationDbContext dbContext)
    {
        InvoiceRepository = invoiceRepository;
        UserRepository = userRepository;

        _dbContext = dbContext;
    }

    public async Task BeginTransaction()
    {
        await _dbContext.Database.BeginTransaction();
    }

    public async Task Commit()
    {
        await _dbContext.Database.Commit();
    }

    public async Task Rollback()
    {
        await _dbContext.Database.Rollback();
    }
}

Unit of Work kasutamine teenustes

Kolmekihilise arhitektuuri korral ei paista andmekihi klassid esitluskihti ning seega kasutavad neid ainult äriloogikakihis olevad teenused. Järgnev näide illustreerib UoW ja transaktsioonide kasutamist arvete teenuse klassis.

public class InvoiceService : IInvoiceService
{
    private readonly IMapper _mapper;
    private readonly UnitOfWork _uow;

    public InvoiceService(IMapper mapper, UnitOfWork uow)
    {
        _mapper = mapper;
        _uow = uow;
    }

    public async Task AddInvoie(InvoiceDto dto)
    {
        var invoice = new Invoice();

        _mapper.Map(dto, invoice);

        try
        {
            await _uow.BeginTransaction();
            await _uow.InvoiceRepository.Save(invoice);
            await _uow.Commit();
        }
        catch
        {
            await _uow.Rollback();
            throw;
        }
    }

    // Ülejäänud meetodid
}