⬆️ ⬇️

How not to use the Repository pattern

image


This article is a kind of experience that was acquired as a result of a very unpleasant architectural error that I made during the long development of a project on Laravel5 .



I will try to tell you how the Repository pattern was used in the project, what advantages and disadvantages were revealed, how it affected the development as a whole, and what profit was obtained.



Introduction



I just want to warn you that the article is more likely focused on developers who are only familiar with the design patterns, read smart books, and then try to apply the whole thing, so to speak, in production. I will mention the development using frameworks that use ActiveRecord (for example, Yii, Laravel, etc.), because, thanks to ActiveRecord, I continue to step on the rake and learn to solve various problems.



Repository pattern



Literally in a few words I propose to consider what the Repository is .

A repository is the concept of storing a collection for entities of a particular type.


You can read more about this pattern:

')



In general, there is a lot of information and it is quite easy to understand what a Repository is.



Starting from Repository



If you have developed medium and / or large (not in terms of workload, but rather with a large code base and long-term support) projects, then you most likely have encountered shortcomings and problems that arise when using ActiveRecord. The main ones can be highlighted in a small list:





ActiveRecord naturally has advantages as well, but we will not mention them, since this is beyond the scope of our article. And at the same time, if in a few words, then: “ActiveRecord is quick, simple and easy.”



So, for several years of working with frameworks based on ActiveRecord, I have most likely come across all its flaws. And somehow having read through clever books and articles, while designing the architecture of a new project, I decided to implement the Repository pattern.



Based on its simple implementation and description, everything is simple: we take interfaces, bind them into classes, which will be our repositories that retrieve data from the “repository”. Everything is fine, at any moment we can bind another Repository, replace the implementation of sampling methods, in general everything is clear.



I went to change the status on Systems Architect

image

Is your “repository” really a Repository?



And then there was a moment when I really needed to replace the implementation. I came to the office with a smile and with the thoughts: “As I’m all easy to submenu, I’ll just create another class and change the string during binding”.



However, the task was to replace the selection in the first case from a file, and in another from a third-party API. When I began to dig and understand all this business, I noticed that my "repositories" return models. Yes, that's right, my allegedly the Repository pattern returns all the same models that continue to walk around the entire project.



Yes, thanks to the interface, I was really able to easily replace the implementation, but the format of the returned data has changed. Previously, it was an instance class with ActiveRecord, but now my repository could return an array or collection.



What does it mean? This means that any representative of my team could use the individual features of the “model”. For example, mutators or accessors, or write a method in a model with logic and call it anywhere. Therefore, by replacing the implementation, I changed the data format and now I cannot guarantee that the entire application will work as it worked, just as anything could have happened. Starting from referring to the innocuous method of the model anywhere, and ending with the save () call in the form. No one knows, no one remembers, especially if the project was experienced by several developers who left and were replaced by new ones.



Do not panic, tests



Then I remembered that you can run the tests. I turned to my partner and asked: “did you write the tests?”. He in turn turned to another colleague and clarified the same question. In general, as it turned out, not a very large% of our application was covered with tests.



What we have?



So, we have an additional layer of abstraction, which requires a larger entry threshold and more time to develop with efficiency aspiring at 0, since the models both walked around the project and continue to walk.



Went to change status to Junior Assistant



In more detail we understand a problem



Understanding the system more deeply, interrupting the examples, I noticed that many developers make such mistakes and even worse.



It may not be good, but I want to present a similar example of a poor implementation of the Repository pattern .



Looking through the information on the topic, I found the following application on Laravel:



https://github.com/Bottelet/Flarepoint-crm/



Let's look at the example UserRepository:



https://github.com/Bottelet/Flarepoint-crm/blob/develop/app/Repositories/User/UserRepository.php



One of the methods I want to make out here (in case all this disappears):



... public function create($requestData) { $settings = Settings::first(); $password = bcrypt($requestData->password); $role = $requestData->roles; $department = $requestData->departments; $companyname = $settings->company; if ($requestData->hasFile('image_path')) { if (!is_dir(public_path(). '/images/'. $companyname)) { mkdir(public_path(). '/images/'. $companyname, 0777, true); } $settings = Settings::findOrFail(1); $file = $requestData->file('image_path'); $destinationPath = public_path(). '/images/'. $companyname; $filename = str_random(8) . '_' . $file->getClientOriginalName() ; $file->move($destinationPath, $filename); $input = array_replace($requestData->all(), ['image_path'=>"$filename", 'password'=>"$password"]); } else { $input = array_replace($requestData->all(), ['password'=>"$password"]); } $user = User::create($input); $user->roles()->attach($role); $user->department()->attach($department); $user->save(); Session::flash('flash_message', 'User successfully added!'); //Snippet in Master.blade.php return $user; } ... 




Probably all, if you analyze such examples in more detail, you can find many more interesting things.



This is a typical example when people do not themselves reach patterns, but recite smart books and pop their repositories, etc. ...


How to use Repository correctly?





Implementing a Repository Pattern or Something Like That



I found an article that describes the implementation of the Repository pattern for Laravel5 (most likely for Yii2 it will be about the same). However, in my personal opinion, it rather describes a structured approach to writing queries using ActiveRecord. On the one hand, it is convenient, duplicate code decreases, models lose weight and the architecture is more elegant. On the other hand, the Repository does not quite fulfill its role as an “abstract repository”, since work is going on with models and full binding to ActiveRecord with all its magic.



The danger may be as follows: when changing the data source (note, it is not necessary to change the database or framework, it is enough to get data from another resource, for example from a third-party API or by making a complex custom query using the query builder), if you worked with models , and the new implementation will return an array or collection, then most likely you will not be able to guarantee the stable operation of your application. So, as you simply do not know (if the project is large and is written not only by you), what methods, accessors / mutators and other delights of the models were used and where.



image


findings



Having obtained a useful and at the same time bitter experience in designing an application, for myself I can emphasize the following conclusions that I want to share (maybe it will be useful to someone):



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



All Articles