Reputation: 1625
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
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