In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-25 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article shows you the role and design of the anti-corrosion layer on the client side in .NET. The content is concise and easy to understand, which will definitely brighten your eyes. I hope you can get something through the detailed introduction of this article.
1. Background introduction
As the current enterprise application architecture is changing to the direction of SOA, the purpose is to divide a huge business system according to business, no matter from the company's management, product development, this series of processes are correct. SOA does bring a solution to the rapid expansion of large-scale enterprise applications.
But what this article wants to say is that we all turn our attention to the back-end, that is, the server side, and focus our energy and time on the architecture design of the back-end service, and gradually ignore the architecture design of the display side. However, the logic of the display side is becoming more and more complex, and the light and thin architecture of the display side has emerged that it is difficult to cope with the rapid expansion of back-end service interfaces. Service interfaces are increasing exponentially. Basically, every new business requirement is to provide new interfaces, which is no problem. According to the design principles of the service, the service interface should have a clear role, rather than thinking according to the code to consider the design of the interface.
However, the problem is whether the structure of the display side of the combination of these interfaces is consistent with this change, and whether it is ready for this change to bring complex logic on the display side.
According to my own experience, I found that the architecture design of the display side is not taken seriously, the emphasis here is not whether the boss attaches importance to it, but that we developers do not pay attention to it, of course, the question of time is excluded here. I have observed a lot of user interface project architecture, the structure is very simple, no encapsulation, no reuse, do not see any design principles. This will make it difficult for these codes to have the impact of service interfaces with the rapid growth of the business, and the biggest question here is whether we, as programmers, have a sense of rapid refactoring. I like this professional quality of programmers very much. It allows us to quickly and quickly keep up with the changes in project structure brought about by the development of our business.
Iterative refactoring has a subtle effect on the project, refactoring can not be too early or too late, just when needed. My experience with refactoring is that when you are poorly written in the face of new features, this is a refactoring signal, and this should be the best time for refactoring. Refactoring is not dedicated to preparing time, but interspersed with the process of writing your code, and it is part of your coding. So I think that's the reason why TDD is accepted.
Corruption of display-side architecture under 2.SOA architecture
The architecture of the display side is corrupted. I personally think there are two problems. First, the original structure of the display side can work well in the traditional system architecture, but now the overall architecture has changed, so it needs to be adjusted in time. Second, the architecture of the display side can not be reconstructed in time, and the structure of the display side can not be further separated, so the display logic can be tested independently.
In this way, with the continuous increase of SOA interfaces, the display side directly embeds the method of invoking services into the display logic, such as the controller of ASP.NET Mvc and ASP.NET Webapi, including the DTO conversion between the two levels.
According to the context design method of DDD, the user display side can also selectively create a display-oriented domain model. This model mainly deals with the pre-processing of the domain after it is about to arrive at the server side. After all, a domain entity has many responsibilities, and if you can build a lightweight domain model on the display side, it will be of great benefit to the reconstruction of display logic, as long as you have complex domain logic. (my previous company (the well-known American e-commerce platform), their display side has complex domain logic, just one display side is surprisingly complex, if we can introduce the domain model display side context on this basis, it will be very good for complex logic processing, of course, this is just my unverified guess, just for reference. )
Those who are interested in the processing of the display-side domain model can refer to my two articles on this aspect:
.net application architecture design-query-oriented domain-driven design practice (adjusting the traditional three-tier architecture, plus maintenance-based business switches)
.net Application Architecture Design-query Service oriented Parametric query Design (decomposing business points and configuring their respective data query contracts separately)
The original clean display logic has a lot of irrelevant service invocation details, as well as a lot of transformation logic and judgment logic, which originally do not belong to this place, so it is very helpful for the reconstruction and reuse of display logic to put them in the right place.
If you do not move it out of the display logic, then with the continuous increase and expansion of the service interface, it will directly cause you to modify the display logic code, if your display logic code is MVC, Webapi common logic, then the situation will be more complicated, and finally the display logic will be occupied by the conversion between ViewModel and Service Dto, it is difficult to find valuable logic.
3. Effective use of anti-corrosion coating to isolate fragment services leads to logical corruption on the display side
The way to solve these problems is to introduce the anti-corrosion coating. Although the original intention of the anti-corrosion layer is to solve the transformation between domain models during system integration, I think there are many similarities between the current system architecture and integration. We can properly learn from these good design methods to solve similar problems.
After the introduction of the anti-corrosion layer, all the code that should not appear in the display logic is transferred to the anti-corrosion layer, and an OO mechanism is established in the anti-corrosion layer, so that these OO objects can be used with the display logic.
Figure 1:
The user layer is divided into three sublayers, UiLayer,Show Logic Layer,Anticorrosive Layer, and the last one is the interface group of the service. All service interface calls need to go from the anti-corrosion layer.
We need to migrate the service invocation and type conversion code in Show Logic Layer to Anticorrsoive Layer, where the conversion logic can be objectified or not, depending on whether the project needs it or not. If the business is really complex, then we need to objectify it in order to encapsulate and reuse.
4. Stripping the technical components of service invocation to rely on interfaces
The first thing to do is to reconstruct the service objects in the logical code into interface-oriented, and then let their dynamic dependencies be injected into the logical types. In ASP.NETWEBAPI, we basically write the display logic here, and I will use this way to demonstrate the examples in this chapter, but if your MVC project and WEBAPI project share the display logic, you need to propose it to form a separate project (Show Logic Layer).
Using OrderManager.Port.Models;using System.Collections.Generic;using System.Web.Http; namespace OrderManager.Port.Controllers {public class OrderController: ApiController {[HttpGet] public OrderViewModel GetOrderById (long oId) {OrderService.Contract.OrderServiceClient client = new OrderService.Contract.OrderServiceClient (); var order = client.GetOrderByOid (oId); if (order = = null) return null; return AutoMapper.Mapper.DynamicMap (order) }}}
This is a simple piece of code that invokes the Order service, first instantiating a client proxy contained in a service contract, and then invoking the remote service method GetOrderByOid (long oId) through the proxy. Perform a simple judgment and finally output OrderViewModel.
If all the logic were that simple, I don't think there would be any anticorrosion layer. Display code like this type is extremely simple. My goal here is not to show how complex the code is written, but to restructure the layer interface of the service call code and inject it into the OrderController instance. The goal is to be able to unit test the controller in subsequent iterative refactoring, which may be troublesome, but it is still needed for long-term benefits.
Using OrderManager.Port.Component;using OrderManager.Port.Models;using System.Collections.Generic;using System.Web.Http; namespace OrderManager.Port.Controllers {public class OrderController: ApiController {private readonly IOrderServiceClient orderServiceClient; public OrderController (IOrderServiceClient orderServiceClient) {this.orderServiceClient = orderServiceClient;} [HttpGet] public OrderViewModel GetOrderById (long oId) {var order = orderServiceClient.GetOrderByOid (oId) If (order = = null) return null; return AutoMapper.Mapper.DynamicMap (order);}}
In order to dynamically inject into the controller at run time, you need to do some basic work to extend the initialization code of the MVC controller. This allows us to do a complete unit test of the OrderController.
As I just said, if the display logic is so simple, then everything will be fine. The real display logic is very complex and changeable. Not all type conversions can be solved using dynamic mapping tools such as Automapper, and some types of conversions have logic inside. The GetOrderById (long oId) method is used to demonstrate the refactoring service invocation component here.
In most cases, we need to combine multiple service calls and return multiple results to the front end. The Items attribute type OrderItem in the OrderViewModel object here contains a Product type attribute. Normally, we only need to obtain the order entry, but sometimes we do need to return the specific product information in the entry to the foreground for partial information display.
Using System.Collections.Generic; namespace OrderManager.Port.Models {public class OrderViewModel {public long OId {get; set;} public string OName {get; set;} public string Address {get; set;} public List Items {get; set;}
The Items property in OrderViewModel is a List collection, so let's look at the OrderItem property.
Using System.Collections.Generic; namespace OrderManager.Port.Models {public class OrderItem {public long OitemId {get; set;} public long Pid {get; set;} public float Price {get; set;} public int Number {get; set;} public Product Product {get; set;}
It contains an instance of Product, and sometimes the attribute needs to be assigned a value.
Namespace OrderManager.Port.Models {public class Product {public long Pid {get; set;} public string PName {get; set;} public long PGroup {get; set;} public string Production {get; set;}
Some of the information in the product type is mainly used to be more user-friendly when presented as an order item. You can only give a product ID, and you can't let the user know which specific product it is.
Let's move on to an example of code ballooning as the business changes, in which we need to get complete information about Product based on the Pid in OrderItem.
Using OrderManager.Port.Component;using OrderManager.Port.Models;using System.Collections.Generic;using System.Web.Http;using System.Linq; namespace OrderManager.Port.Controllers {public class OrderController: ApiController {private readonly IOrderServiceClient orderServiceClient; private readonly IProductServiceClient productServiceClient; public OrderController (IOrderServiceClient orderServiceClient, IProductServiceClient productServiceClient) {this.orderServiceClient = orderServiceClient; this.productServiceClient = productServiceClient } [HttpGet] public OrderViewModel GetOrderById (long oId) {var order = orderServiceClient.GetOrderByOid (oId); if (order = = null & & order.Items! = null & & order.Items.Count > 0) return null Var result = new OrderViewModel () {OId = order.OId, Address = order.Address, OName = order.OName, Items = new System.Collections.Generic.List ()} If (order.Items.Count = = 1) {var product = productServiceClient.GetProductByPid (order.Items [0] .Pid); / / call a single API for obtaining goods if (product! = null) {result.Items.Add (ConvertOrderItem (order.Items [0], product)) }} else {List pids = (from item in order.Items select item.Pid) .ToList (); var products = productServiceClient.GetProductsByIds (pids) / / call the API if (products! = null) {result.Items = ConvertOrderItems (products, order.Items); / / convert OrderItem types}} return result in batch } private static OrderItem ConvertOrderItem (OrderService.OrderItem orderItem, ProductService.Contract.Product product) {if (product = = null) return null Return new OrderItem () {Number = orderItem.Number, OitemId = orderItem.OitemId, Pid = orderItem.Pid, Price = orderItem.Price, Product = new Product () {Pid = product.Pid, PName = product.PName PGroup = product.PGroup, Production = product.Production}} } private static List ConvertOrderItems (List products, List orderItems) {var result = new List (); orderItems.ForEach (item = > {var orderItem = ConvertOrderItem (item, products.Where (p = > p.Pid = = item.Pid). FirstOrDefault ()); if (orderItem! = null) result.Add (orderItem) }); return result;}}
My first feeling is that display logic is basically type conversion code, and I didn't add any display logic here, which makes the code swell rapidly in such cases. It is conceivable that if we add display logic to these codes, it is basically difficult for us to maintain these display logic in the later stage, and these display logic is the real responsibility of this class.
The resulting problem is that the important logic is submerged in these conversion codes, so we urgently need a location that can accommodate these conversion codes, that is, the anti-corrosion layer. We deal with these conversion logic specifically in the anti-corrosion layer. Of course, my example here is relatively simple, only contains queries, the real anti-corrosion layer is very complex, it has to deal with things no less than other levels of logic processing. We are just converting some DTO objects here instead of complex DomainModel objects.
5. Put the conversion between the DTO of the service and the ViewModel of the display side into the anti-corrosion layer
We need an anti-corrosion layer to handle the conversion code, including the call logic to the back-end service, and moving this part of the code into the anti-corrosion object will be very helpful for our later refactoring.
Namespace OrderManager.Anticorrsive {using OrderManager.Port.Component; using OrderManager.Port.Models; using System.Collections.Generic; using System.Linq; / OrderViewModel anticorrosion object / public class OrderAnticorrsive: AnticorrsiveBase, IOrderAnticorrsive {private readonly IOrderServiceClient orderServiceClient; private readonly IProductServiceClient productServiceClient; public OrderAnticorrsive (IOrderServiceClient orderServiceClient, IProductServiceClient productServiceClient) {this.orderServiceClient = orderServiceClient This.productServiceClient = productServiceClient;} public OrderViewModel GetOrderViewModel (long oId) {var order = orderServiceClient.GetOrderByOid (oId); if (order = = null & & order.Items! = null & & order.Items.Count > 0) return null Var result = new OrderViewModel () {OId = order.OId, Address = order.Address, OName = order.OName, Items = new System.Collections.Generic.List ()} If (order.Items.Count = = 1) {var product = productServiceClient.GetProductByPid (order.Items [0] .Pid); / / call a single API for obtaining goods if (product! = null) {result.Items.Add (ConvertOrderItem (order.Items [0], product)) }} else {List pids = (from item in order.Items select item.Pid) .ToList (); var products = productServiceClient.GetProductsByIds (pids) / / call the API if (products! = null) {result.Items = ConvertOrderItems (products, order.Items); / / convert OrderItem types}} return result in batch } private static OrderItem ConvertOrderItem (OrderService.OrderItem orderItem, ProductService.Contract.Product product) {if (product = = null) return null Return new OrderItem () {Number = orderItem.Number, OitemId = orderItem.OitemId, Pid = orderItem.Pid, Price = orderItem.Price, Product = new Product () {Pid = product.Pid, PName = product.PName PGroup = product.PGroup, Production = product.Production}} } private static List ConvertOrderItems (List products, List orderItems) {var result = new List (); orderItems.ForEach (item = > {var orderItem = ConvertOrderItem (item, products.Where (p = > p.Pid = = item.Pid). FirstOrDefault ()); if (orderItem! = null) result.Add (orderItem) }); return result;}}
If you feel necessary, you can put IOrderServiceClient and IProductServiceClient interfaces into the AnticorrsiveBase base class.
5.1. The conversion logic is programmed and written directly in the method of anticorrosive coating.
For the design of the anti-corrosion layer, in fact, if you do not have much conversion code and the business is relatively simple, I suggest that it is easier to write the code directly into the program. It is also relatively simple and convenient to use some reusable code directly using static extension methods. The biggest problem is that it is not conducive to continuous refactoring in the later stage. We cannot predict future business changes, but we can use refactoring to solve the problem.
5.2. The transformation logic is objectified, and the encapsulation and reuse structure is established to prevent further corruption.
Correspondingly, you can objectify the conversion code to form anticorrosive objects, each of which is specially used to deal with the data acquisition and conversion logic of a certain business point. if you have data transmission logic, you will benefit greatly from the anticorrosion object. After objectification, you can directly subscribe to the dependency injection events of the relevant controllers. If you are procedural code, it will be inconvenient for you to complete dynamic conversion, sending and acquisition.
6. Two dependent inversion Design methods of anticorrosive coating
Let's take a look at how to let the anticorrosion object automate the service invocation and delivery without interference. We hope that the anticorrosion object is performing the anticorrosion duty completely transparently, and we don't want it to bring much overhead to our implementation.
6.1. Event-driven (anti-corrosion layer listens to display logical events)
We can use events to implement the observer pattern, allowing the anti-corrosion layer object to listen for an event and automatically handle an action when the event is triggered, rather than the manual call to display.
Namespace OrderManager.Anticorrsive {public interface IOrderAnticorrsive {void SetController (OrderController orderController); OrderViewModel GetOrderViewModel (long oId);}}
Order anticorrosion object interface, which contains a void SetController (OrderController orderController); important method, which is used to allow antiseptic objects to automatically register events.
Public class OrderController: ApiController {private IOrderAnticorrsive orderAnticorrsive; public OrderController (IOrderAnticorrsive orderAnticorrsive) {this.orderAnticorrsive = orderAnticorrsive; this.orderAnticorrsive.SetController (this); / / set controller to anticorrosion object} public event EventHandler SubmitOrderEvent; [HttpGet] public void SubmitOrder (OrderViewModel order) {this.SubmitOrderEvent (this, order);}}
In the controller, whenever we have a business action, we just trigger the event, of course, mainly to send data, the query can directly call the method of the object. Because the anticorrosion object acts as a bridge to integrate with the background service, when submitting an order, you may need to call many background service methods at the same time, so it is convenient to use event handling.
/ OrderViewModel anticorrosion object / public class OrderAnticorrsive: AnticorrsiveBase, IOrderAnticorrsive {public void SetController (OrderController orderController) {orderController.SubmitOrderEvent + = orderController_SubmitOrderEvent;} private void orderController_SubmitOrderEvent (object sender, OrderViewModel e) {/ / submit order logic}} 6.2. Dependency injection interface
The dependency injection interface is entirely used to isolate the controller from the anticorrosion object. In the above code, I defined the interface in the anticorrosion object layer, so the project where the controller object is located needs to refer to the anticorrosion layer. It will seem a bit irrelevant when dealing with events and methods at the same time. There are both interfaces and methods. In fact, this is a balance. The purer things are, the more they have to pay some price.
If we define a pure dependency injection interface for anti-corrosion objects to implement, we need special methods to trigger events when they are triggered, because events that are not in this class cannot be triggered.
The above content is what the function and design of the client-side anticorrosive layer is in .NET. Have you learned the knowledge or skills? If you want to learn more skills or enrich your knowledge reserve, you are welcome to follow the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.