In this article, I will tell you, using the evolution of my project as an example, the history of transition and the vision of contract programming.
First I wanted to name the article - “Contract programming”, insofar as the approach used is to divide all business logic into data contracts and service clients using these contracts and interacting with each other through these data structures, so that one and the same The structure is successfully processed in different services.
Something I will describe in my own language.
The story of my transition to contract programming began with the fact that I had an overgrown legacy project with a large set of different functionality within the same business area. Plus, all this was still multithreading and customization for different clients. Specifically, the business area was the exchange of electronic legally relevant documents between the accounting system, file and cloud services.
In the process of developing the system, new business requirements appeared and as a result, the project quickly developed and was complicated by new functionality, which at some stage led to a situation where the project architecture began to resemble several separate applications with related functionality. Only this functionality was implemented not from common elements, but from similar, but differently implemented structures in different assemblies and namespaces.
Visually, such an architecture can be represented as several verticals of functionality
A characteristic feature of such an architecture was that if any additional requirement or error appeared, then it was required to implement such a task in all “functional modules” of the system, and not in one area. Another characteristic feature was the presence in the project of a large number of builders, which lead to such a vertical division of the business area. The developer implements new algorithms for new business requirements in the code of builders, which leads to the emergence of new entities with different structures for the same business object. Admittedly, this approach can be considered more object-oriented than a simple code-behind approach with spaghetti code. Thus, if you see a large number of builders in your projects, then the transition to a contact approach will simplify support and accelerate the development of your project.
Once, at the company, we were faced with the fact that the client whom we were implementing began to express that the system that I developed often produced errors. I could not argue with this, although errors appeared periodically, but they were fixed and some of the clients had the system installed and used it. I must say thank you that the client was harmful and picky, and very carefully tested our system during the implementation period.
I myself also did not like the fact that I did not have enough time for development and support, in spite of a lot of efforts, and at some point I simply had no idea what else could improve the system and make it more stable.
The decision came suddenly and unexpectedly.
I well understood that the same business objects are implemented differently in different modules of the system - documents of different types, receipts, workers, service clients, database clients, file clients with the ability to store documents in various formats (customization for different clients;) )
Part of the logic was implemented in entities, which prevented the use of the same entities in different algorithms.
Then I decided - we need to create something like a single constructor with details from which all the algorithms implemented in the system can be assembled. Which is logical, because it reduces the number of similar classes, and simplifies support.
I decided to strictly divide the "universe" of my application into
- data contracts - data structures that contain and are used only for data storage
Documents
Envelopes
Receipts
EP
...
- clients-services - I specifically write that clients are services, because when implementing each system that interacted in the business area, I represented it as an envelope on this system, in which the methods for working with date contracts were implemented. And besides the fact that these classes are wrappers over services, they also contained the necessary binding logic, conversions and transformations to simplify business logic
In addition, loggers clung to these clients. That allowed you to quickly find the point of error by the log. (in the future, a lot of calls disappeared when, by the logs, users themselves began to understand where they had a mistake - a file system, a network, an accounting system or a web service of an operator)
for example
CloudClient - a client of a cloud service to which documents are sent and processed
- Download
- send
- Receive
- Download
- To approve
...
FileSystemClient - file system client
ERPClient - client accounting system
The clients implemented all the methods previously used in the algorithms in all modules, which made it possible to quickly and easily implement newly implemented algorithms from the same parts.
And the allocation of similar functionality methods in the respective clients led to greater specialization and reuse of code.
Architecture was now a diagram as in the picture
I allocated all the basic objects of my designer to the assembly with the end of the SDK (Software Developers Kit) and it became easier to work and most importantly more pleasant. The work has already ceased to be the creation of crutches and temporary solutions, and now the attitude to the project from the inside has become more serious from the fact that I now saw a huge supply of scalability and competitive architecture. This is to the fact that this fact significantly increased motivation and opened up new professional horizons.
Based on the created SDK, I handed over to implement a layer of business logic that automates the implementation of direct business tasks. Since all the technical nuances were brought to the lower layers located in the clients, the implementation of the same algorithms was simplified and began to look much more pleasant.
The approach is simple, and simplifies further development, and what is very important is to increase the flexibility of the architecture. Yes, this will require extensive refactoring. In my case, it took 2 weeks of development in a fanatical mode. But after that, I almost did not code, because everything worked stably.
And thanks to the separation of responsibilities (SPR), and logs, I was able to quickly suppress errors of third-party services, entailing errors on my side. Previously, this required analytics that ate considerable time.
This approach is good to use on certain layers. On higher layers of the application, an anemic model may be more convenient, only it is better to implement an anemic model based on a contractual approach.