SOA distributed software architecture combined with Domain Driven Design

1, Definition of SOA and DDD

SOA and DDD are common system architectures, but the core of them is different.

SOA (service-Oriented Architecture) was proposed by Gartner in 1996. It is a distributed software architecture. It can deploy, combine and use loosely coupled coarse-grained application components through the network according to requirements. In short, SOA is an architecture for large-scale system development. In the system based on SOA architecture, the functions of specific applications are built by the combination of some loosely coupled components (i.e. services) with unified interfaces. It is aimed at the data exchange between multi-core and multi platforms.

DDD (Domain Driven Design) was proposed by Eric Evans in 2004. Its core content is "how to map business domain concepts to software engineering". It overturns the old habit of "software development and design from the data layer", emphasizes the powerful power of domain model in software, and pays attention to how to transform the complex business processes inside the enterprise into software.

It may be considered that SOA is aimed at the overall architecture of large-scale systems, focusing on how to separate projects, isolate development, and finally realize system consolidation. DDD is a development management process for a single project, focusing on how to use domain model to transform business requirements into software. There is no theoretical conflict between the two. If we can combine the two, show their strengths and give full play to their respective advantages.

2, Hierarchical structure of DDD

1. Concept

Conceptually, domain driven design architecture is mainly divided into four conceptual layers: infrastructure layer, domain layer, application layer and presentation layer.

Infrastructure layer: it is built to provide various general technical capabilities for each layer. It can provide domain layers such as Hibernate, LINQ and ADO Net and other persistence mechanisms, deliver messages for the application layer, provide plug-ins for the presentation layer, and so on.

Domain layer: it is the core part of the system and represents the logical concept of business. It will customize the business information and business rules according to the actual business process, and formulate the domain model according to a certain relationship. Although domain models need to be saved by the infrastructure layer, the logical relationship between domain models is isolated from the infrastructure layer. Even if the infrastructure layer is converted from NHibernate technology to LINQ technology, it will not affect the structure of the domain layer. The domain model will only rely on the actual business logic, and it will only change flexibly according to the change of business.

Application layer: its task is to coordinate the relationship between the domain layer and the presentation layer. It can also be used as a bridge between the system and the outside world. There will be no business logic in this layer. This layer plays an important role in SOA service-oriented architecture, which will be described in detail in Section 7.

Presentation layer: it is a commonly used interface development, which can be expressed in the form of pages (ASP.NET, JSP), windows (WinForm, WPF, Swing), etc. its main responsibility is to communicate information with users. (Note: in general project development, Web services will be placed in the presentation layer as an interface to communicate with the outside world, but in SOA, Web services will be mostly placed in the application layer, which will be further explained below)

2. Development examples

Here is a common example of order management, which will be used as a reference in the following chapters:

Each user will have a corresponding account in the Person table, which records the user's name, address, telephone, Point and other basic information.

The process of each transaction is recorded in the Order table. The delivery fee of each commodity is 10 yuan. When the Price of the commodity exceeds 98 yuan, you can deliver the goods free of charge. When the user's Person Point exceeds 2000 points, you can get a 70% discount, 10% off for 1000 ~ 2000 points, and 10% off for less than 1000 points. Finally, the overall Price is (TotalPrice).

At the time of final settlement, the Order number and Delivery date will be generated in the Order table, and the points of the total Order price will also be added to the points in the Person table.

Finally, the OrderItem table contains attributes such as Goods, Price and purchase quantity Count. It mainly records the detailed transaction status of each order.

The above business logic is basically similar to Taobao, Dangdang and other large shopping websites. The reason why we use such an example as a reference is to show how DDD uses the domain model to adapt to the changeable business logic relationship.

3, Transform business relationships into domain models

1. Concept

Model-driven design (MODEL-DRIVEN-DESIGN) is the core of DDD. It represents the relationship between various objects and transforms complex logical relationships into models.

Models are mainly divided into three types: Entity, Value Object and Service.

Entity: an entity contains not only a series of attributes, but also the connection with events. Changes in the environment and events in a life cycle will cause changes within the entity. It seems that in the entity Order, the Point of Person and the Price of OrderItem will directly affect the total Price, and the total Price will also affect the freight Freightage, etc. Everything in the Order entity will be affected by external factors such as Person and OrderItem. Such objects are regarded as entities. At different times, entities have different states, so in the development process, we need to add an "identifier" to the entity to distinguish the identity of the object, which is the only sign in the life cycle of the entity.

Value object: when the object used has only attributes and no other logical relationships, we can treat it as a value object. The value object has no state and does not need to have an identifier. In most cases, it can exist inside an entity as an attribute. In general, the properties of value objects cannot be changed. When the properties need to be changed, you can delete the whole object and then add a new object.

Service: when there are some operations between entities, which are not attached to a single entity, but are associated with multiple entities, services can be used to encapsulate these operations. It is worth noting that services do not refer to web services alone, nor do they exist only in the domain layer, but there will be services in each layer, and the services of each layer have different functions. In the infrastructure layer, services may be used to build authentication, e-mail, error handling and other operations; At the domain level, service is more often an operation, which is used to coordinate the relationship between multiple entities and deal with various business problems; In the application layer (especially in the distributed development system), services are more than web services, TCP/IP sockets, MSMQ and so on. Here, services will be used as an interface to communicate with the outside world;

  • Note: there is also some controversy. Eric believes that entities only represent the relationship between multiple objects, and their actions will be reflected by services, which is called anemia model. But in the development process, more and more people will add actions to entities, which is called blood filling model. In fact, different problems should be objectively analyzed and treated separately. In this example, services will be developed according to Eric's definition. In the later development process, you can also reflect the benefits brought by the service layer.

2. Example description

Start with ADO Net Entity Framework implementation model, Person and Order belong to two entities respectively. They will inherit the Root interface and generate a Guid as a flag in their life cycle. Here, the OrderItem is placed in the Order entity as a value object, which means that the OrderItem will be obtained through the Order, and the outside world cannot directly obtain the OrderItem across the Order. Of course, this should be determined by the specific business situation. When the outside world needs to call the OrderItem class separately, it should consider making the OrderItem an entity class independently.

Here, you can add the Guid attribute to the entity by using the segment class. For the detailed introduction of the segment class and method, please refer to C# comprehensive disclosure - segment class and method

namespace Business.DomainModel
{
    public interface Root {
    }

    public partial class Order:Root
    {  
        private Guid _guid;        
        
        public Order()
        {
            _guid = System.Guid.NewGuid();
        }

        //Set a unique Guid for the root object;
        public Guid GUID
        {
            get { return _guid; }
        }
    }

    public partial class Person:Root
    {
        public Person()
        {
            _guid = System.Guid.NewGuid();
        }

        //Set a unique Guid for the root object;
        private Guid _guid;

        public Guid GUID
        {
            get { return _guid; }
        }
    }
}

IV. detailed description of Repository

1. Concept

Repository is a way to transform persistent objects into domain models. It can be used to obtain, update and manage the life cycle of persistent objects. It decouples the application from the Persistence technology. The program does not need to be subject to the use of Oracle or MySql database, nor will it be subject to Hibernate, LINQ and ADO Net and other data layer constraints, so that developers can focus on the domain model.

The Repository is a little similar to the DAL layer of the traditional three-tier model, but the Repository divides the boundary for each root object. In this example, both Person and Order will have corresponding PersonRepository and OrderRepository. The Order and Repository attributes of the inserted items are only deleted. When a connection is established between multiple objects, the relationship will be complex, especially in LINQ. The program can easily obtain the value of OrderItem through the navigation attribute of Person, and finally it is easy to make the code confused. Therefore, establishing the boundary of Repository can effectively manage the functions of each Repository.

2. Example description

Note that the access and deletion of OrderItem are included in the OrderRepository. When obtaining and modifying the order, the "explicit loading" context will also be used Order. The method of include ("OrderItem") enables the OrderItem to be updated synchronously. Through personrepository Getperson (int) gets the Person object. Its internal order attribute will be null, which must be clearly divided according to the boundary of the domain model.

When LINQ comes out, the acquisition of data becomes simple. Especially in some small system development, many people will unconsciously break the decomposition rules of this domain model. However, with the complexity of the system, the problem will gradually appear. For example, when the attribute of the Order object is updated, use orderrepository After update (Order) updates the database, the Person object on the page fails to update synchronously. When Person exchanges data with the database, the Order is changed back to the old value.

This situation is very common in chaotic data layer development, so we will adhere to the principle of Repository and clearly divide the functions of Repository according to the domain model.

namespace Business.IRepository
{
    public interface IOrderRepository
    {
        Order GetOrder(int id);
        IList<Order> GetList();
        IList<Order> GetListByPerson(int personID);
        int AddOrder(Order order);
        int DeleteOrder(int id);
        int UpdateOrder(Order order);
      
        int AddOrderItem(OrderItem orderItem);
        int DeleteOrderItem(int id);
    }

    public interface IPersonRepository
    {
        int AddPerson(Person person);
        int AttachPerson(Person person);
        int UpdatePerson(Person person);
        Person GetPerson(int id);
        IList<Person> GetList();
    }
}

namespace Business.Repository
{
    public class OrderRepository:IOrderRepository
    {
        //Get Order by single ID
        public Order GetOrder(int id)
        {
            BusinessContext _context = new BusinessContext();
            Order order = null;

            try
            {
                using (TransactionScope scope = new TransactionScope())
                { 
                    //Since OrderItem is an attribute in the Order entity, it must be obtained synchronously through OrderRepository
                    var list = _context.Order.Include("OrderItem")
                        .Where(x => x.ID == id);
                    if (list.Count() > 0)
                        order = list.First();
                    else
                        order = new Order();
                    scope.Complete();
                }
            }
            catch (Exception ex)
            {
                //Error handling and return an empty object
                Business.Common.ExceptionManager.DataException.DealWith(ex);
                order = new Order();
            }
            _context.Dispose();
            return order;
        }
        ..................
        ..................
    }

    public class PersonRepository:IPersonRepository
    {
        public int AddPerson(Person person)
        {
            return LinqHelp.Add<Person>(person);
        }

        public Person GetPerson(int id)
        {
            return LinqHelp.Get<Person>(id);
        }
        .................
        .................
    }
}

When updating a complex domain model such as Order, it will be troublesome to distinguish whether a single OrderItem attribute is a new value or an updated value, and then deal with it separately. Moreover, OrderItem is only a value object, and attributes such as ID code have no practical significance to it. Therefore, when updating the list < OrderItem > attribute, it will be deleted and then loaded again. This is a very effective method when the number of OrderItems is small.

namespace Business.Repository
{
    public class OrderRepository:IOrderRepository
    {
         .................
         .................
        //Update the Order, because it is difficult to distinguish which are the original OrderItem s and which are newly inserted
        //Using a simple method, the original OrderItem will be deleted and then reinserted
        public int UpdateOrder(Order order)
        {
            int returnValue = -1;
            BusinessContext _context = new BusinessContext();
           
            try
            {
                using (TransactionScope scope = new TransactionScope())
                {
                    var list = _context.Order.Include("OrderItem")
                        .Where(x => x.ID == order.ID);
                    if (list.Count() > 0)
                    {
                        //Update Order column
                        Order _order = list.First();
                        _order.Count = order.Count;
                        _order.Delivery = order.Delivery;
                        _order.Favorable = order.Favorable;
                        _order.Freightage = order.Freightage;
                        _order.OrderNumber = order.OrderNumber;
                        _order.PersonID = order.PersonID;
                        _order.Price = order.Price;
                        _order.TotalPrice = order.TotalPrice;

                        //Delete the original order detail item OrderItem
                        if (list.First().OrderItem.Count != 0)
                            foreach (var item in list.First().OrderItem)
                                DeleteOrderItem(item.ID);
                        //Add a new order detail item OrderItem
                        if (order.OrderItem.Count != 0)
                        {
                            foreach (var item in order.OrderItem)
                            {
                                var _orderItem = new OrderItem();
                                _orderItem.Count = item.Count;
                                _orderItem.Goods = item.Goods;
                                _orderItem.OrderID = item.OrderID;
                                _orderItem.Price = item.Price;
                                AddOrderItem(_orderItem);
                            }
                        }
                        returnValue = _context.SaveChanges();
                    }
                    else
                        returnValue = 0;

                    scope.Complete();
                }
            }
            catch (Exception ex)
            {
                Business.Common.ExceptionManager.DataException.DealWith(ex);
                returnValue=-1;
            }

            _context.Dispose();
            return returnValue;
        }

        //Insert OrderItem
        public int AddOrderItem(OrderItem orderItem)
        {
            return LinqHelp.Add<OrderItem>(orderItem);
        }

        //Delete OrderItem
        public int DeleteOrderItem(int id)
        {
            EntityKey key = new EntityKey("BusinessContext.OrderItem", "ID", id);
            return LinqHelp.Delete(key);
        }
    }
}

5, Domain level services

1. Example description

The second section has basically introduced the role of services. The role of domain layer services is mainly to solve business logic problems. More often, services are business-related actions. For example, in the above example:

The process of each transaction is recorded in the Order table. The delivery fee of each commodity is 10 yuan. When the Price of the commodity exceeds 98 yuan, you can deliver the goods free of charge. When the user's Person Point exceeds 2000 points, you can get a 70% discount, 10% off for 1000 ~ 2000 points, and 10% off for less than 1000 points. Finally, the overall Price is (TotalPrice).

This complex business logic can be completely completed by a domain service class AccountManager

namespace Business.Service.DomainService
{
    public class AccountManager
    {
        private Person _person;
        private Order _order;

        public AccountManager(Person person, Order order)
        {
            _person = person;
            _order = order;
        }

        ///Calculate the overall charge 
        public void Account()
        {
            //Calculate the quantity of goods
            GoodsCount();
            //Calculate commodity prices
            PriceAccount();
            //Calculate preference level
            FavorableAccount();
            double price1 = (_order.Price - _order.Favorable).Value;
            //Calculate freight
            FreightageAccount(price1);
            //Calculate the overall price and fee
            _order.TotalPrice = price1 + _order.Freightage.Value;
        }

        //Calculate the quantity of goods
        private void GoodsCount()
        {
            _order.Count=0;
            foreach (var OrderItem in _order.OrderItem)
                _order.Count += OrderItem.Count;
        }
 
        //Overall commodity price
        private void PriceAccount()
        {
            _order.Price = 0;
            foreach (var OrderItem in _order.OrderItem)
                _order.Price += OrderItem.Price * OrderItem.Count;
        }

        //The discount is divided into three grades. There is a 10% discount for points less than 1000, a 20% discount for points less than 2000 and a 7% discount for points greater than 2000
        private void FavorableAccount()
        {
            int point = (int)_person.Point.GetInt();

            if (point < 1000)
                _order.Favorable = _order.Price * 0.1;
            if (point >= 1000 && point < 2000)
                _order.Favorable = _order.Price * 0.2;
            if (point > 2000)
                _order.Favorable = _order.Price * 0.3;
        }

        //If the price is more than 98 yuan, the freight can be exempted. The remaining freight is 10 yuan
        private void FreightageAccount(double price)
        {
            if (price >= 98)
                _order.Freightage = 0;
            else
                _order.Freightage = 10;
        }
    }
}

You might say that in this business process, in addition to the point discount, person Except point, other businesses are only related to the attribute of Order. According to the scheme of blood filling model, these businesses can be put into the method of Order and the point discount can be independent into a service. However, in many development processes, it is found that attaching actions to the model will bring a series of problems. It seems that you don't know which operations should be implemented on the model actions and where they should be implemented in services. The endless debate will not stop because of a small example here, but here I will insist on using the anemia model and using services to complete all actions.

Another example: when the Order is finally settled, the Order number and the Order date Delivery will be generated in the Order table, and the points of the total Order price will also be added to the points in the Person table. For this operation, you can also develop a PaymentManager service class separately for management.

namespace Business.Service.DomainService
{
    public class PaymentManager
    {
        //Order settlement
        public void Payment(Order order,Person person)
        {
            //Confirm the order and establish the order number
            order.OrderNumber = Guid.NewGuid().ToString();
            order.Delivery = DateTime.Now;
            //Increase points
            if (person.Point.HasValue)
                person.Point += (int)order.TotalPrice.GetValueOrDefault();
            else
                person.Point = (int)order.TotalPrice.GetValueOrDefault();
        }
    }
}

Using the services of the domain layer, the functions of each Manager service class are very clear, and the business management is very convenient. The domain layer can change flexibly with the change of business. Moreover, the domain layer has the characteristics of "high cohesion and low coupling". It does not rely on any other layer, but only includes the business logic.

6, Factory mode factory

Factory is a commonly used software development mode. The information of development modes such as simple factory, factory method and abstract factory can be found everywhere on the Internet, but this is not the subject of domain driven design. In this section, I mainly want to introduce the use timing of factory.

Not all objects need to be generated in Factory mode. When generating simple objects, you can directly use constructors to replace factories, or add Factory methods to generate objects. However, if there are complex business rules between internal attributes when generating objects, the generation method can be independent of a Factory class. At this time, the client does not need to ignore the potential logical relationship, but directly generates the corresponding objects through this Factory.

For example, when creating a new Order, the business stipulates that the freight is 1% of the total amount and the discount is 7.50%. If the client creates a new object Order and negative values for these attributes, the relevant business logic will be exposed. At this time, the Factory mode can be used to encapsulate the relationship between attributes into the Factory, and the client can easily generate the Order object through the Factory without paying attention to the complex internal relationship.

As for the more complex Factory mode, I won't introduce it here. You can find relevant information on the Internet.

7, Detailed application layer

1. Characteristics of application layer in SOA system

When developing SOA distributed system, the application layer is a key point, which has two main functions.

First, the main function of the application layer is to coordinate the work of the domain layer and direct the domain objects to solve business problems, but the application layer itself will not involve the business state.

Second, in the SOA system, the application layer is the data transportation center and the port of information distribution, which is responsible for data conversion and data sending and receiving.

It has the following characteristics:

  • Coarse grain size

Distributed system is different from ordinary websites and applications, because it assumes that the outside world has no understanding of the internal system. Users only want to input relevant data and finally get a series of calculation results. Therefore, we should encapsulate the calculation results in a data transmission object (DTO) to realize coarse-grained transmission, which is the most obvious difference between general projects and SOA systems in the service layer. Think about it. If a page needs to display the personal data of a customer and the details of an Order at the same time, it will get the information of Person, Order and OrderItem at the same time. In the development process of ordinary systems, this will not cause too many problems, but when using remote services, if you use three methods to obtain them respectively, it will cause a lot of performance loss. Especially in the distributed development system, the application layer and presentation layer are separated. More often, they are modules developed by different departments. The presentation layer will not understand the logical relationship in the application layer. In the view of the presentation layer, Person, Order and OrderItem are the same thing, that is, the return value. Therefore, in the system, the information of multiple tables should be encapsulated in a DTO object and returned at one time through a remote method in the application layer. The use of coarse-grained data elements is a feature of distributed systems.

  • Transmissibility

If you are familiar with SOA systems, the word DTO (Data Transfer Object) must not be unfamiliar. DTO is a carrier of data transmission. There is no business logic inside. Through DTO, the internal domain objects can be isolated from the outside world. DTO encapsulates the data of the client, so its design is more for the needs of the client than business logic. For example, originally, the relationship between Person and Order is one to many, but when a page only displays the single Order information of a customer, we can design the relationship between Person and Order in DTO as one to one as needed. If you are using MVC to develop a general website, you will more often directly convert the returned object into a Model. If you are developing a distributed system, you will think more about system performance and hidden business logic. Moreover, considering that it will be troublesome to convert internal objects into DTO, it is suggested to consider the compatibility of DTO so that DTO can be used as the return carrier of multiple methods. (Note: in the SOA system, the transmissibility of coarse-grained elements should be given priority from the perspective of performance)

  • Encapsulation

In the SOA system, the publishing of application layer services does not need a complex model. It only needs to use Facade to encapsulate some functions in a few service classes, and publish them to the outside world by using Web Service, TCP/IP socket, MSMQ and other service methods.

At this point, I really appreciate the help Mr. Martin has given me. In the development process, these complex problems have brought me a lot of trouble. Mr. Martin's experienced and unique insights really inspire me.

2. Application layer coordination

The application layer service will use the Repository to complete the basic operations of entity insertion, update and acquisition, and call the business logic of service management in the domain layer. Note that all business logic will only be hidden in the domain layer, and the application layer services only play a coordinating role. They should not contain any business logic.

We can see that OrderService completes a series of complex business logic such as checkout and payment by calling domain layer services such as AccountManager and PaymentManager.  

3. Data conversion process

The role of DTO has been explained earlier, but the conversion between domain objects and DTO is a complex event. Therefore, a data converter can be established to realize this function.

In ordinary work, not many people will make the "order management system" into the SOA mode, because in the distributed system, the format and definition of data are mostly agreed between departments, including clear rules. However, due to the limitations of conditions, I still want to take order management as an example here. I hope it can bring you some help. Examples are as follows: in shopping cart checkout, the page will contain user basic information, current order information, order details and other parts.

 

 

To complete data conversion, you can first establish DTO objects according to the page. In distributed systems, DTO objects are usually placed in an independent namespace, which is called business TransferObject. DTO objects are often created for the needs of the presentation layer. Here, because the presentation layer page only needs the data of a single user and a single order, the OrderDTO object will contain user information and order data, and there is also an order detail column list < orderitemdto >. Of course, the design of DTO can be modified as required.

In SOA system, DTO is the carrier of remote service data, so it will be attached with serializable feature. In this example, the data contract of WCF will be used to implement OrderDTO and OrderItemDTO.

 

 

As shown in the figure, to realize data conversion, a data converter should be established. Here, OperationAssembler is a data converter. It is the core of data conversion. It is a tool to realize the conversion between domain objects and DTO. To realize data conversion between multiple objects is really a very troublesome thing, so I have always advocated paying attention to the compatibility of DTO objects, so that a single DTO object can be applied to multiple appearance layers, so as to reduce the trouble caused by data conversion.

namespace Business.Service.ApplicationService
{
    public class OperationAssembler
    {
        //Convert domain objects into DToS
        public static OrderDTO GetOrderDTO(Order order,Person person)
        {
            OrderDTO orderDTO = new OrderDTO();
            if (person != null)
            {
                orderDTO.EMail = person.EMail.GetString();
                orderDTO.Address = person.Address.GetString();
                orderDTO.Name = person.Name.GetString();
                orderDTO.PersonID = person.ID;
                orderDTO.Point = person.Point.GetInt();
                orderDTO.Telephone = person.Telephone.GetString();
            }
            if (order != null)
            {
                orderDTO.PersonID = order.PersonID;
                orderDTO.Count = order.Count.GetInt();
                orderDTO.Delivery = order.Delivery.GetDateTime();
                orderDTO.Favorable = order.Favorable.GetDouble();
                orderDTO.Freightage = order.Freightage.GetDouble();
                orderDTO.OrderID = order.ID;
                orderDTO.OrderNumber = order.OrderNumber.GetString();
                orderDTO.Price = order.Price.GetDouble();
                orderDTO.TotalPrice = order.TotalPrice.GetDouble();
                var orderItemList = order.OrderItem.ToList();
                if (orderItemList.Count != 0)
                {
                    var orderItemDTO = new List<OrderItemDTO>();
                    foreach (var orderItem in orderItemList)
                        orderItemDTO.Add(GetOrderItemDTO(orderItem));
                    orderDTO.OrderItemList = orderItemDTO;
                }
            }
            return orderDTO;
        }

        public static OrderItemDTO GetOrderItemDTO(OrderItem orderItem)
        {
            OrderItemDTO orderItemDTO = new OrderItemDTO();
            orderItemDTO.Count = orderItem.Count.GetInt();
            orderItemDTO.Goods = orderItem.Goods.GetString();
            orderItemDTO.OrderID = orderItem.OrderID;
            orderItemDTO.OrderItemID = orderItem.ID;
            orderItemDTO.Price = orderItem.Price.GetDouble();
            return orderItemDTO;
        }

        //Convert DTO to multiple objects
        public static void SetOrder(OrderDTO orderDTO, out Person person, out Order order)
        {
            person = new Person();
            person.EntityKey=new System.Data.EntityKey("BusinessContext.Person","ID",orderDTO.PersonID);
            person.Address = orderDTO.Address;
            person.EMail = orderDTO.EMail;
            person.ID = orderDTO.PersonID;
            person.Name = orderDTO.Name;
            person.Point = orderDTO.Point;
            person.Telephone = orderDTO.Telephone;

            order = new Order();
            order.EntityKey=new System.Data.EntityKey("BusinessContext.Order","ID",orderDTO.OrderID);
            order.Count = orderDTO.Count;
            if (orderDTO.Delivery.Year!=0001&&orderDTO.Delivery.Year!=9999)
                order.Delivery = orderDTO.Delivery;
            order.Favorable = orderDTO.Favorable;
            order.Freightage = orderDTO.Freightage;
            order.ID = orderDTO.OrderID;
            order.OrderNumber = orderDTO.OrderNumber;
            order.PersonID = orderDTO.PersonID;
            order.Price = orderDTO.Price;
            order.TotalPrice = orderDTO.TotalPrice;
            var orderItemDTOList = orderDTO.OrderItemList;
            if (orderItemDTOList.Count() != 0)
                foreach (var orderItemDTO in orderItemDTOList)
                    order.OrderItem.Add(GetOrderItem(orderItemDTO));
        }

        public static OrderItem GetOrderItem(OrderItemDTO orderItemDTO)
        {
            OrderItem orderItem = new OrderItem();
            orderItem.EntityKey = new System.Data.EntityKey("BusinessContext.OrderItem", "ID", orderItemDTO.OrderItemID);
            orderItem.Count = orderItemDTO.Count;
            orderItem.Goods = orderItemDTO.Goods;
            orderItem.ID = orderItemDTO.OrderItemID;
            orderItem.OrderID = orderItemDTO.OrderID;
            orderItem.Price = orderItemDTO.Price;
            return orderItem;
        }
    }
}

//Data transmission object DTO
namespace Business.TransferObject
{
    [DataContract]
    public class OrderItemDTO
    {
        private int _orderItemID;
        private int _orderID;
        private string _goods;
        private double _price;
        private int _count;

        [DataMember]
        public int OrderItemID
        {
            get { return _orderItemID; }
            set { _orderItemID = value; }
        }
        ............
        ............
    }

    [DataContract]
    public class OrderDTO
    {
        private int _personID;
        private string _name;
        private string _address;
        private string _telephone;
        private int _point;
        private string _email;
        private int _orderID;
        private string _orderNumber;
        private int _count;
        private double _freightage;
        private double _favorable;
        private DateTime _delivery;
        private double _price;
        private double _totalPrice;
        private IList<OrderItemDTO> _orderItemDTOList;

        [DataMember]
        public int PersonID
        {
            get{return this._personID;}
            set{this._personID=value;}
        }
        ..........
        ..........
    }
}

Through the data converter, the transformation between domain model and DTO can be realized smoothly, and the operation of application layer services can be coordinated.

4. Release of application layer

When developing an SOA system, the application layer service needs to be opened to the outside world using remote methods. When receiving the request, it can call the domain layer service to obtain the operation result, then convert the operation result into DTO through the data converter OperationAssembler, and finally return it to the presentation layer. At first, I tried to establish a remote interface corresponding to the objects of each application layer, but after many refactorings, I think the travel object is a simple external interface, and there is no logical relationship between the objects. Therefore, a simpler method is to use the appearance mode to establish a few remote service classes and include all the methods of application layer objects.

 

Note the code. OperationService includes all operations on the Person model and Order model. Moreover, each operation simply calls the application service to obtain the calculation results, and then uses the operation assembler to convert the data. There is no business logic in it.

namespace Business.Service.ApplicationService
{
    [ServiceContract]
    public interface IOperationService
    {
        [OperationContract]
        int AddOrder(ref OrderDTO orderDTO);

        [OperationContract]
        int DeleteOrder(OrderDTO orderDTO);

        [OperationContract]
        int UpdateOrder(ref OrderDTO orderDTO);

        [OperationContract]
        IList<OrderDTO> GetOrderByPerson(int personID);

        [OperationContract]
        OrderDTO GetOrder(int orderID);

        [OperationContract]
        int AddPerson(ref OrderDTO orderDTO);

        [OperationContract]
        int UpdatePerson(ref OrderDTO orderDTO);

        [OperationContract]
        OrderDTO GetPerson(int personID);

        [OperationContract]
        IList<OrderDTO> GetPersonList();

        [OperationContract]
        OrderDTO Payment(int orderID);
    }

    public class OperationService:IOperationService
    {
        [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
        public int AddOrder(ref OrderDTO orderDTO)
        {
            OrderService orderService = new OrderService();
            Order order = GetOrder(orderDTO);
            int n = orderService.AddOrder(order);
            orderDTO = OperationAssembler.GetOrderDTO(order, null);
            return n;
        }

        [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
        public int DeleteOrder(OrderDTO orderDTO)
        {
            OrderService orderService = new OrderService();
            return orderService.DeleteOrder(GetOrder(orderDTO));
        }

        [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
        public int UpdateOrder(ref OrderDTO orderDTO)
        {
            OrderService orderService = new OrderService();
            Order order = GetOrder(orderDTO);
            int n = orderService.UpdateOrder(order);
            orderDTO = OperationAssembler.GetOrderDTO(order, null);
            return n;
        }
        ..............
        ..............
    }
} 

8, Overall system architecture

1. Architecture embodying Domain Driven Design

To summarize the overall structure of Domain Driven Design DDD, the Repository layer uses ORM mapping or SQL commands to convert the persistent data into domain objects, and then designs the corresponding domain layer service Domain Service according to the business logic. Then the application layer coordinates the operation, completes the business needs by using Repository, domain model and Domain Service, and then converts the Domain Object domain object into data transmission object DTO through data converter. Finally, the Application Service of the application layer is opened to the outside world by using remote communication technology.

Note that in the SOA system, the UI presentation layer and Application Service application layer services are separated, and the presentation layer can call multi-party remote services to complete the work at the same time.

 2. Reflect the architecture of service-oriented development

The architecture of service-oriented development SOA is mainly reflected in the separation between the presentation layer and the application layer through remote communication. The presentation layer can refer to multi-party application services as the basis. Thus, the system realizes the separation of business, different functional modules can be developed independently, and finally reflected in the performance layer through services. With the long-term development, many enterprises develop a set of independent systems for a single functional module, and then provide services for third parties through powerful virtualization technology, which is the predecessor of cloud computing.

Just like a communication shopping platform, it is actually a comprehensive platform integrating multiple services such as internal business management, bank transfer service, call center, third-party interface and so on. If you have experience in this field, you will know that bank transfer and call center are just a few simple interfaces provided by banks, telecommunications, mobile and other companies. Developers don't need to pay attention to the internal structure. They can call it through a few simple remote methods. This is the best embodiment of application layer service.

3. Conclusion

The purpose of writing this article is to share with you some of my experience in the development process. You are welcome to comment and point out the shortcomings.

In fact, architecture is a dead thing, and talents are creatures with brains. Each architecture must have its advantages and disadvantages. We should experience it together from the development rather than blindly follow it. I hope my humble opinions can help you. Don't forget to support it.

 

Tags: Java Programming Big Data Distribution architecture

Posted by marcela1637 on Wed, 18 May 2022 13:32:18 +0300