Skip to content

Commit

Permalink
Merge pull request #2 from swisschain/unit-of-work
Browse files Browse the repository at this point in the history
Added option to start non-transactional unit of work
  • Loading branch information
KonstantinRyazantsev committed Aug 15, 2020
2 parents 66b9fab + 64e0ca0 commit 8b8913f
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 5 deletions.
18 changes: 16 additions & 2 deletions src/Swisschain.Extensions.Idempotency/IUnitOfWork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,47 @@ namespace Swisschain.Extensions.Idempotency
{
public interface IUnitOfWork : IAsyncDisposable
{
/// <summary>
/// Valid only for the <see cref="IsTransactional"/> unit of work.
/// </summary>
IOutboxWriteRepository OutboxWriteRepository { get; }
bool IsTransactional { get; }
bool IsCommitted { get; }
bool IsRolledBack { get; }
/// <summary>
/// Valid only for the <see cref="IsTransactional"/> unit of work.
/// </summary>
Outbox Outbox { get; }

/// <summary>
/// Initializes transactional unit of work
/// </summary>
Task Init(IOutboxDispatcher defaultOutboxDispatcher,
IOutboxWriteRepository outboxWriteRepository,
Outbox outbox);

/// <summary>
/// Closes the <see cref="Outbox"/> and commits unit of work transaction
/// Closes the <see cref="Outbox"/> and commits unit of work transaction.
/// Valid only for the <see cref="IsTransactional"/> unit of work.
/// </summary>
Task Commit();

/// <summary>
/// Rollbacks unit of work transaction
/// Rollbacks unit of work transaction.
/// Valid only for the <see cref="IsTransactional"/> unit of work.
/// </summary>
Task Rollback();

/// <summary>
/// Dispatches <see cref="Outbox"/> using default <see cref="IOutboxDispatcher"/>.
/// It's recommended to use more specific dispatcher if possible.
/// Valid only for the <see cref="IsTransactional"/> unit of work.
/// </summary>
Task EnsureOutboxDispatched();

/// <summary>
/// Dispatches <see cref="Outbox"/> using specified <paramref name="dispatcher"/>.
/// Valid only for the <see cref="IsTransactional"/> unit of work.
/// </summary>
Task EnsureOutboxDispatched(IOutboxDispatcher dispatcher);
}
Expand Down
8 changes: 8 additions & 0 deletions src/Swisschain.Extensions.Idempotency/IUnitOfWorkFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ namespace Swisschain.Extensions.Idempotency
public interface IUnitOfWorkFactory<TUnitOfWork>
where TUnitOfWork : UnitOfWorkBase
{
/// <summary>
/// Should create transactional unit of work
/// </summary>
Task<TUnitOfWork> Create(Outbox outbox);

/// <summary>
/// Should create non-transactional unit of work
/// </summary>
Task<TUnitOfWork> Create();
}
}
8 changes: 8 additions & 0 deletions src/Swisschain.Extensions.Idempotency/IUnitOfWorkManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ namespace Swisschain.Extensions.Idempotency
public interface IUnitOfWorkManager<TUnitOfWork>
where TUnitOfWork : UnitOfWorkBase
{
/// <summary>
/// Begins the transactional unit of work
/// </summary>
Task<TUnitOfWork> Begin(string idempotencyId);

/// <summary>
/// Begins either non-transactional unit of work
/// </summary>
Task<TUnitOfWork> Begin();
}
}
50 changes: 47 additions & 3 deletions src/Swisschain.Extensions.Idempotency/UnitOfWorkBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,40 @@ namespace Swisschain.Extensions.Idempotency
public abstract class UnitOfWorkBase : IUnitOfWork
{
private IOutboxDispatcher _defaultOutboxDispatcher;
private Outbox _outbox;
private IOutboxWriteRepository _outboxWriteRepository;

public IOutboxWriteRepository OutboxWriteRepository { get; private set; }
public IOutboxWriteRepository OutboxWriteRepository
{
get
{
if (!IsTransactional)
{
throw new InvalidOperationException("Outbox repository of the non-transactional unit of work can't be accessed");
}

return _outboxWriteRepository;
}
private set => _outboxWriteRepository = value;
}

public bool IsTransactional => Outbox != null;
public bool IsCommitted { get; private set; }
public bool IsRolledBack { get; private set; }
public Outbox Outbox { get; private set; }

public Outbox Outbox
{
get
{
if (!IsTransactional)
{
throw new InvalidOperationException("Outbox of the non-transactional unit of work can't be accessed");
}

return _outbox;
}
private set => _outbox = value;
}

protected abstract Task CommitImpl();
protected abstract Task RollbackImpl();
Expand All @@ -30,6 +59,11 @@ public Task Init(IOutboxDispatcher defaultOutboxDispatcher,

public async Task Commit()
{
if (!IsTransactional)
{
throw new InvalidOperationException("Non-transactional unit of work can't be committed");
}

if (IsCommitted)
{
throw new InvalidOperationException("The unit of work was commit already");
Expand All @@ -50,6 +84,11 @@ public async Task Commit()

public async Task Rollback()
{
if (!IsTransactional)
{
throw new InvalidOperationException("Non-transactional unit of work can't be rolled back");
}

if (IsCommitted)
{
throw new InvalidOperationException("The unit of work was commit already");
Expand All @@ -72,13 +111,18 @@ public Task EnsureOutboxDispatched()

public async Task EnsureOutboxDispatched(IOutboxDispatcher dispatcher)
{
if (!IsTransactional)
{
throw new InvalidOperationException("Outbox of the non-transactional unit of work can't be dispatched");
}

await Outbox.EnsureDispatched(dispatcher);
await OutboxWriteRepository.Update(Outbox);
}

public async ValueTask DisposeAsync()
{
if (!(IsCommitted || IsRolledBack))
if (IsTransactional && !(IsCommitted || IsRolledBack))
{
await Rollback();
}
Expand Down
7 changes: 7 additions & 0 deletions src/Swisschain.Extensions.Idempotency/UnitOfWorkManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,12 @@ public async Task<TUnitOfWork> Begin(string idempotencyId)

return unitOfWork;
}

public async Task<TUnitOfWork> Begin()
{
var unitOfWork = await _unitOfWorkFactory.Create();

return unitOfWork;
}
}
}

0 comments on commit 8b8913f

Please sign in to comment.