📜 ⬆️ ⬇️

We write maintainable code

We have hundreds of software projects on support, some of them have been supported by us for almost ten years. It is not difficult to guess that the concept of maintainable code (I will translate this concept as easy-to-support code) is one of our main ones. By a happy coincidence, the easy-to-support code is also easy to (unit-) testing, easy to learn by new team members, etc. Most likely, this is due to the fact that to create maintainable code you have to attend to the good architecture of the project and start some good habits.
In this article we will talk about such habits, thanks to which often a good architecture is obtained by itself. I will also try to illustrate everything with good examples.


But I'll start by telling you what I don't like about some abbreviations for the letter M: MVC, MVP, MVVM and others. For a neophyte who reads his first books and articles on how to design applications, these abbreviations are among the first. He creates a false impression that a program is a kind of triad consisting, for example, of a model, controller and presentation, and, what is most dangerous, the members of this triad are equal in importance! Many of these articles and video tutorials reinforce this dangerous lie with examples of applications from the series: “Well, let us have such a template for presentation, the controllers are the controllers of our framework, and the model is some ActiveRecord ORM”. After this, it may take years for Fat, Dumb Ugly Controllers to make the neophyte realize that he is doing something wrong. That the Model in these triads takes the main place and the more complex the application, the more pronounced it is.

The main principle of dividing a program into high-level parts has not changed for several decades: Data access layer, Business (logic) layer and Presentation layer . Moreover, it is obvious that the layer reflects the essence and the whole value of our application is the Business layer , and DAL and PL are some kind of service layers. And all these abbreviations for the letter M are architectural patterns that describe how to organize the Presentation layer in programs, nothing more.
')
Well, since I promised to talk about habits, let's highlight the first : in the race for fashionable data storage technologies or to present them to the user, do not forget that your application is your Business layer, the rest is a husk that changes easily with time.

And immediately, without prefaces, the second good habit : SOLID. I don’t know what the rest of SOLID does, but the importance of the S ingle responsibility principle cannot be overestimated. I would call it a necessary and sufficient condition for a good code. In any bad code there is always a class that does more than one thing (Form1.cs or index.php, thousands of lines each, probably, everyone saw, or even did). The rest of the principles of SOLID are not so important for me and, by the way, recently on Habré there was a good article on this topic, to which you are sending. I largely agree with what was written there and thank the author of this article for not having to explain it myself.
The principle of single responsibility (further simply the principle of S) literally makes you write quality code and many, very many techniques are simply tools for writing code that satisfies this principle.
And an example is what I highlight in the third good habit : Dependency Injection. I have little idea of ​​a more or less large project, professing the principle of S, without DI. I promised to give examples and here is a good place to start doing this. The usual class, which is the logic of working with orders of any online store.

public class OrderService
{
    public void Create(...)
    {
        //  
        
        //   email    
        var smtp = new SMTP();
        //  smtp.Host, UserName, Password   
        smtp.Send();
    }
}

, , , , — OrderController . , , . OrderService SMTP! . :

public class OrderService
{
    private SmtpMailer mailer;
    public OrderService()
    {
        this.mailer = new SmtpMailer();
    }

    //  -      ,       
}

public class SmtpMailer
{
    public void Send(string to, string subject, string body)
    {
        //      SMTP
    }
}

, S, , OrderService , «» . : , OrderService , , , , SmtpMailer. . IMailer

public interface IMailer
{
    void Send(string to, string subject, string body);
}

SmtpMailer . - IoC-, , IMailer SmtpMailer. OrderService :

public class OrderService
{
    private IMailer mailer;
    public OrderService(IMailer mailer)
    {
        this.mailer = mailer;
    }

    //  -      ,       
}

? OrderService SmtpMailer . IMailer OrderService mailer, , , , . , , OrderService .
— , . , -. : ( -!), — IoC- SmtpMailer NewTechnologyMailer — ( !). . . , , - , . , — . , . , — , OrderService !

: (, ).
OrderService. , IOrderRepository, . , 1999 , OrderService . - . , OrderService IOrderService ( Create ) , .

public sealed class OrderService: IOrderService
{
    private IOrderRepository repository;
    private IMailer mailer;
    public OrderService(IOrderRepository repository, IMailer mailer)
    {
        this.repository = repository;
        this.mailer = mailer;
    }

    public void Create(...)
    {
        var order = new Order();
        //   ,     -. ,   ..
        this.repository.Save(order);

        this.mailer.Send(<orders user email>, <subject>, <body with order details>);
    }
}

, mailer Create . -: workflow - . , , , , - . maintenance. -: — . , , , IUserParametersService. , , ITranslator ( ). , . . Injection happy. IoC- , . , - . , S. , OrderService . Event Driven . :

namespace <base namespace>.Events
{
[Serializable]
public class OrderCreated
{
    private readonly Order order;

    public OrderCreated(Order order)
    {
        this.order = order;
    }

    //  C#     {get; private set},        
    public Order GetOrder()
    {
        return this.order;
    }
}
}

« ». , OrderService . .

namespace <base namespace>.EventHandlers
{
public class OrderCreatedEmailSender : IEventHandler<OrderCreated>
{
    public OrderCreatedEmailSender(IMailer, IUserParametersService, ITranslator)
    {
        //        
    }

    public void Handle(OrderCreated event)
    {
        this.mailer.Send(...);
    }
}
}

Serializable . , , , (Redis, ActiveMQ ). -, , -. , ().

, OrderService, ( -, , ), , DI. - , . . Find usages IDE OrderCreated .

, . DI Events, , IOrderRepository. - — , . OrderCreated. , : .

, . , . , . , , , , , . , - -. . , . , , S ;-)

P.S. , . , . , , IoC, Event-Driven — . , . « », . , , . , , .

Source: https://habr.com/ru/post/261863/


All Articles