Fortune
Fortune

Reputation: 1625

How to persist entity with business rules?

I'm using the ASP.NET Boilerplate for my project. And I have an entity as shown in the code snippet below.

public class Transaction : FullAuditedEntity<Guid>, IMustHaveTenant
{
    protected Transaction()
    {
        TransactionState = TransactionState.Uncompleted;
    }

    public TransactionState TransactionState { get; protected set; }
    public virtual Loan Loan { get; protected set; }
    public int TenantId { get; set; }
    // ...

    public async Task CompleteAsync(ICoreBankingService coreBankingService, IRepository<Transaction, Guid> transactionRepository)
    {
        try
        {
            // Perform a series of compulsory actions in some given sequence with coreBankingService that might throw exception
            Loan.SetSomeStuffThatOriginatedFromMotherTransaction();
            TransactionState = TransactionState.Completed;
        }
        catch (Exception ex)
        {
            // Log the exception and set this Transaction entity state appropriately
            TransactionState = TransactionState.Failed;
        }
        finally
        {
            // Make sure by all means to persist the resulting the entity within itself
            await transactionRepository.UpdateAsync(this);
        }
    }
}

I understand I should separate persistence from the entity (which by the way, is an architecture provided by ASP.NET Boilerplate out of the box! using Application Services).

However, I NEED to make certain that I perform a series of compulsory actions in some given sequence with coreBankingService and persist changes at each stage of these computations on the Transaction entity, hence the reason for my naive and probably wrong approach.

Please, what is the right approach to this kind of problem? How do I persist states of an entity that results from a computation or action within the same entity?

Upvotes: 2

Views: 183

Answers (1)

aaron
aaron

Reputation: 43093

You can expose internal methods that change the state:

public class Transaction : FullAuditedEntity<Guid>, IMustHaveTenant
{
    protected Transaction()
    {
        TransactionState = TransactionState.Uncompleted;
    }

    public TransactionState TransactionState { get; protected set; }
    public virtual Loan Loan { get; protected set; }
    // ...

    internal void Abort()
    {
        TransactionState = TransactionState.Failed;
    }

    internal void Complete()
    {
        TransactionState = TransactionState.Completed;
    }
}

And define the domain service in the same assembly:

public class TransactionManager : DomainService
{
    private readonly ICoreBankingService _coreBankingService;
    private readonly LoanManager _loanManager;
    private readonly IRepository<Transaction, Guid> _transactionRepository;

    public TransactionManager(
        ICoreBankingService coreBankingService,
        LoanManager loanManager,
        IRepository<Transaction, Guid> transactionRepository)
    {
        _coreBankingService = coreBankingService;
        _loanManager = loanManager;
        _transactionRepository = transactionRepository;
    }

    public async Task TryCompleteAsync(Transaction transaction)
    {
        try
        {
            // Use _coreBankingService to do something that might throw exception
            _coreBankingService.DoSomething();
            _loanManager.SetSomeStuffThatOriginatedFromMotherTransaction(transaction.Loan);
            transaction.Complete();
        }
        catch (Exception ex)
        {
            // Log the exception and abort the Transaction
            transaction.Abort();
        }
        finally
        {
            // Make sure by all means to persist the resulting the entity
            await _transactionRepository.UpdateAsync(transaction);
        }
   }
}

Usage:

await _transactionManager.TryCompleteAsync(transaction);

Upvotes: 2

Related Questions