[TinyERP]Angular for Enterprise Application – Multiple data-stores (Part 2: Scale your repository)

0
18

Overview

If you want to checkout source-code used in this article. Please check it our from https://github.com/techcoaching/TinyERP (develop branch please)

In previous article, We know how to use single database for your application. This works well for small application that we have a few GB of data.

For enterprise system, we may have a huge data, storing this in single database server may lead to some performance problems related to IO. Storing a single big database file was not recommended. There are some scenarios for scaling  databases (see How to scale your database – will be published soon), such as partitioning table:

This will split a big tables (for example with 100M records) and store into smaller files with specified rules. This can improve performance a little. We will not discuss about this in this article. Please visit How to scale your database (will be published soon) for more information about this.

There is another way to scale our application as photo below:

As photo above, our application can have more than 1 instance of database. each instance will manage data for specified domain (such as: Order Database will contain data for Order Management – includes: order, order items, …). This will help us improving performance for each better.

For example, product database was changed rarely, we can use 1 server for this.

We also found that the response time from order database is high. For this case, we can consider to add more servers into pool of “Order Database”.

As mentioned that each database should contain specified domain. So what is DOMAIN in this case?

DOMAIN in the simple view, we can understand it as BOUNDED CONTEXT in Domain Driven Design. For example, with Order bounded context, we should contain those entities in the same database:

  • Order master information
  • Order item
  • User order contact information
  • Delivery address

Which problem may we have if stored “Order Item” in “OrderItem database” (not in order database).

Order item only has meaning in context of order bounded context and we have relationship between order and order item also.

Storing those in 2 separated databases will increase the complex of the code when we want to construct Order object with its detail information.

How to implement this in TinyERP

Overview

Let see, the client will create new Order on the UI and send to api for creating and stored in appropriated repository.

 

In this diagram, we have new concepts (such as: OrderHandler instead of OrderController, OrderCommandHandler instead of OrderService as in previous post) as we have already applied Domain Driven Domain (DDD). Please ignore this for now.

What we should focus is how to create new instance of DbContext and insert data into MSSQL using entity framework.

We can see that:

  • Client side will send “CreateOrder” request to server side including necessary information for creating new Order.
  • OrderHandler will act as controller in WebAPI for receiving request and passing to OrderCommandHandler
  • OrderCommandHandler acts as OrderService in multi-layer pattern for validation and create new instance of Order object. Then insert into database by calling to OrderRepository
  •  OrderRepository just inserts new instance of Order into database.

Let see how we can do it in TinyERP.

Start your code

OrderHandler:

This class will receive the request from client and pass to OrderCommandHandler, as below:

namespace App.Api.Features.Order { [RoutePrefix("api/orders")] public class OrderHandler : CommandHandlerController { [Route("")] [HttpPost()] [ResponseWrapper()] public void CreateOrder(CreateOrderRequest request) { this.Execute(request); } } }

We can see that, there is not business logic here. Just receive and pass through the request.

Content of CreateOrderRequest:

public class CreateOrderRequest : BaseCommand { public CustomerDetail CustomerDetail { get; set; } public IList OrderLines { get; set; } }

For CustomerDetail in CreateOrderRequest:

public class CustomerDetail { public string Name { get; set; } }

and Detail of OrderLine, as Order obejct can have multiple OrderLine item:

public class OrderLine { public Guid ProductId { get; set; } public string ProductName { get; set; } public int Quantity { get; set; } public decimal Price { get; set; } }

The Execute method of OrderHandler will call appropriated Handle method of IOrderCommandHandler.

 OrderCommandHandler:

Business logic of creating new order will be implemented in this class.

namespace App.Command.Impl.Order { internal class OrderCommandHandler : BaseCommandHandler, IOrderCommandHandler { public void Handle(CreateOrderRequest command) { OrderAggregate order = AggregateFactory.Create(); order.AddCustomerDetail(command.CustomerDetail); order.AddOrderLineItems(command.OrderLines); using (IUnitOfWork uow = this.CreateUnitOfWork()) { IOrderRepository repository = IoC.Container.Resolve(uow); repository.Add(order); uow.Commit(); order.AddEvent(new OnOrderCreated(order.Id)); } order.PublishEvents(); } } }

In this class, we have Execute method that accepts CreateOrderRequest as parameter. We also create new instance of UnitOfWork base on OrderAggregate. OrderAggregate is AggregateRoot of Order domain.

using (IUnitOfWork uow = this.CreateUnitOfWork())

Then, we can create new instance of OrderRepository and add new instance  of Order into database:

IOrderRepository repository = IoC.Container.Resolve(uow); repository.Add(order); uow.Commit();

I think, this code is easy for understanding. The problem is, which database, this order will be inserted.

Open “App.Common/Configurations/configuration.debug.config”, we have settings for this:

="1.0"="utf-8" <appconfiguration> ... <aggregates> <add name="App.Aggregate.Order.OrderAggregate" repoType="MSSQL" connectionStringName="OrderConnection"></add> </aggregates> <databases> <clear /> <add name="OrderConnection" database="MyERP_Orders" server="." port="1433" userName="sa" password="123456" dbType="MSSQL" default="true"></add> </databases> ... </appconfiguration>

In aggregates configuration, it was telling that for order domain (App.Aggregate.Order.OrderAggregate). Data will be insert using connection named “OrderConnection”.

Look at “databases”, we can see the detail connection string detail for OrderConnection:

<add name="OrderConnection" database="MyERP_Orders" server="." port="1433" userName="sa" password="123456" dbType="MSSQL" default="true"></add>

Behind the scene,  we have OrderDbContext class located in “App.Context”:

public class OrderDbContext: App.Common.Data.MSSQL.MSSQLDbContext, IDbContext { public System.Data.Entity.DbSet OrderAggregates { get; set; } public System.Data.Entity.DbSet CustomerDetails { get; set; } public System.Data.Entity.DbSet OrderLines { get; set; } }

Please aware that OrderDbContext implement “IDbContext”.

So in the case of creating new instance of OrderAggregate (including OrderCustomerDetail and OrserLine also). System will use OrderDbContext.

Photo below, describing how the system looks like if we append product domain:

 Using Multiple domains in the same DbContext

Ideally,  each domain should have its own DbContext object for creating/ updating data in that domain.

There are some cases, the domain is small and we want 2 or 3 small domains to use the same DbContext.

For example, in this case, I will use OrderDbContext for both order domain and product domain.

 System diagram as below:

Let update OrderDbContext from:

public class OrderDbContext: App.Common.Data.MSSQL.MSSQLDbContext, IDbContext { public System.Data.Entity.DbSet OrderAggregates { get; set; } public System.Data.Entity.DbSet CustomerDetails { get; set; } public System.Data.Entity.DbSet OrderLines { get; set; } }

To:

public class OrderDbContext: App.Common.Data.MSSQL.MSSQLDbContext, IDbContext, IDbContext { public System.Data.Entity.DbSet OrderAggregates { get; set; } public System.Data.Entity.DbSet CustomerDetails { get; set; } public System.Data.Entity.DbSet OrderLines { get; set; } public System.Data.Entity.DbSet ProductAggregates { get; set; } public System.Data.Entity.DbSet Categories { get; set; } ... }

Note that, in this new code:

  • OrderDbContext inherits new interface, IDbCOntext
  • Add all entities of product domain into this DbContext

Then, we can use the same as what we did for OrderAggregate object.

 For more information about other articles in this series

Thank you for reading,

Note: Please like and share to your friends if you think this is usefull article, I really appreciate

LEAVE A REPLY