1
\$\begingroup\$

I'm setting up new API to Setup LocalAccounts and need your advice on how to design the API with CQS

some background : LocalAccount Is Account that related to private customer or corporate customer and contain Debit or credit Account details with one or more cards (plastic and 1 virtual ) Example of LocalAccounts : PrivateDebitLocalAccount , CorpoeateCreditLocalAccount, PrivateCreditLocalAccount ...

I want to use CQS to expose command for the Action on those entity : AddNewLocalAccountCommand, UpadteLocalAccountCommand and so on ..

My client doesn't want to work with lots of commands for the same action : 4 command to AddNew for PrivatedebitLocalAccount, CorpoeateCreditLocalAccount, CorpoeateDebitLocalAccount, PrivateCreditLocalAccount and then use 4 command to update and 4 Delete commands ...and so on

So I decided to Expose one Command for create all 4 types of localAccount AddNewLocalAccount that will get flat object with all fields and in the WebAPI inside I map the request to my command (I Do this for Backward comparability on the Contract changes for My command ) and send it to Handler using MediatR library By Jimmy Bogard. In The handler I Call AggrigateLocalAccountServices with 2 dependencies of ServiceFactories to get back the Domain object i need : AccountServiceFactory and CustomerServiceFact, this will help to get the relevant domain object from the command : Customer (private or customer) and Account based on AccountType in the command (credit or debit)

Example of AddNewCorporateLocalAccountRequest

var LocalAccountData = new LocalAccountData(Guid.NewGuid())
{
    CustomerType = CustomerTypes.Corporate,
    AccountTypes = AccountTypes.Credit,
    CreditLimit = 200,
    Deposit = 100,
    CompanyId = "43545",
    MonthlyLimit = 50,
    FirstName = "ddd",
    LastName = "deee",
};

In My Controller WebApi I Maped the Request to command even it is mixed fields from Debit and credit ..

var command = new AddNewLocalAccountCommand(LocalAccountData.Id == 
Guid.Empty ?  Guid.NewGuid() : LocalAccountData.Id)
{
    CompanyId = LocalAccountData.CompanyId, //Corporate field
    CustomerType = LocalAccountData.CustomerType, //define the
    CustomerType (private/Corporate) for LocalAccountFactory (customer)
    AccountTypes = LocalAccountData.AccountTypes,//define the
    AccountType(debit/credit) for AccountFactory (customer)
    CreditLimit = LocalAccountData.CreditLimit, //Credit data
};
//send to commandHandler using Mediatr :
var x = new AddNewLocalAccountCommandHandler(
    new LocalAccountAggregator(
        localAccountServiceFactory, accountFactoryService));

var result = mediatR.Send(command);

In my commandHandler method

public void Handle(AddNewLocalAccountCommand command){
// map again all data to local account DTO so i can ask the factories in AggRoot to extract the domain i need
    var localAccountData = new LocalAccountData(command.Id)
    {
        CustomerType = command.CustomerType,
        AccountTypes = command.AccountTypes,
        Balance = command.Balance,
         ....
    };
    var customerAccontId =
    this._localAccountAggregator
        .AddNewLocalAccount(localAccountData); 
};

My Agg root class I expose interface for adding the localAccount and this is how is the implementation

public virtual Guid AddNewLocalAccount(ILocalAccountData data)
{
    var localAccount = CreateLocalAccount(data);
    var vc = CreateVirtualCard(); 
    localAccount.Accounts.Cards.Add(vc);
    // now I need to decide where to map from localAccount to my EF 
    //   object,  here or  should it be inside the Service that know the 
    // right type of tables to be mapped 
    customerDto = Mapper.Map<localAccount, Customer>(request);
    _context.Customers.Add(customerDto);
    _context.SaveChanges();
    return Guid.NewGuid();
}

private ILocalAccount CreateLocalAccount (ILocalAccountData data)
{
    var accountService =
    _accountFactoryService.GetService(data.AccountTypes);

    var localAccountFactoryService = 
    _localAccountFactoryService.GetService(data);

    //create method will be call from the right LocalAccountService 
      that get the right domain object.
    return localAccountFactoryService.Create(data); 
}

Here is one of the service that can create the right localAccount Type based on the data from client :

public class CorporateLocalAccountService : ILocalAccountService
{
    private IAccountService _account;

    public CorporateLocalAccountService(IAccountService account)
    {
        _account = account;
    }

    public ILocalAccount CreateData(ILocalAccountData data)
    {
        return new CorporateLocalAccount(new Entity(data.Id))
        {
            Account = _account.Create(data),
            Contact = new Person { LastName = data.LastName, FirstName = data.FirstName, MobilePhoneNumber = data.MobilePhone },
            CompanyId = data.CompanyId,
            CompanyName = data.CompanyName,
        };
    }
}

}

I have some question about this design :

  1. My Client Doesn't know my internal Domain so they can do mistake and send fields of debit account in Credit Account request as i Use one container object , Beside sending JSON example of possible valid contract Or Change from Flat fields to object Inside my LocalAccountData : DebitAccountData, CreditAccountData, PrivateData, CorporateData. How can i tell my client not do do mistake in the request? Should I change my API request ?

  2. In my CorporateLocalAccountService I return the domain object to the aggregate , should this class create the data in DB using EF or just return domain model and Aggregate will create it ?

  3. I'm Working with the Command Query and in CommandHander I call the Aggregate Object that need to create localAccount (insert data to 4-5 tables and create virtual Card as a result of creating the LocalAccount and send it back to client ) How Do I work with my EntityFramework ? I mean do I need repositories or should i call the DBContext and use the EF objects inside the Aggregate directly and avoid Repositories ? // I warp the commandHandler with TransactionCommandHandler so Do I need the transaction scope in the Aggregate class or i can use the transaction scope from my commandHandler decorator ?

Thanks

\$\endgroup\$
  • 3
    \$\begingroup\$ is this how the indentation looks in your editor? If not can you edit the question to reflect your actual indentation \$\endgroup\$ – AGirlHasNoName Apr 2 at 0:35
  • \$\begingroup\$ Not sure I understand what is missing , i Just paste part of the code that relevant to my question . \$\endgroup\$ – mreizik Apr 2 at 8:01
  • \$\begingroup\$ The indentation consists of the spaces to the left, at the beginning of the lines. It is important, because it makes it easier to find matching braces and to understand the syntactical structure of the code. Your indentation was wrong - I corrected it. \$\endgroup\$ – Olivier Jacot-Descombes Apr 2 at 13:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.