Anti-pattern permission

I began to notice something like an anti-pattern in my ASP.NET development. This bothers me because I feel it is right to maintain a good design, but at the same time it smells wrong.

The problem is this: we have a multi-level application, the lower level is a class that processes calls to the service that provides us with data. Above this is a layer of classes that can transform, manipulate, and validate data. Above this are the ASP.NET pages.

In many cases, methods from the service level do not need any changes before moving on to the presentation, so the model is a simple passage, for example:

public List<IData> GetData(int id, string filter, bool check) { return DataService.GetData(id, filter, check); } 

This is not wrong and does not have to work terribly, but it creates a strange dependency on copy / paste. I am also working on a core service, and it also copies this pattern a lot, and there are interfaces everywhere. So what happens: "I need to add int someotherID to GetData ", so I add it to the model that calls the service, the service itself and the interfaces. It does not help that GetData actually represents several methods that all use the same signature but return different information. Interfaces help a bit with this repetition, but it still appears here and there.

Is there a name for this anti-pattern? Is there any fix or major architectural change is the only real way? It seems like I need to smooth out my object model, but sometimes the data layer performs the transforms, so it has value. I also like to split the code between “external service calls” and “page data delivery”.

+4
source share
7 answers

I would suggest you use a query object template to solve this problem. Basically, your service can have a signature, for example:

 IEnumerable<IData> GetData(IQuery<IData> query); 

Inside the IQuery interface, you can have a method that accepts a unit of work as an input, for example, a transaction context or something like ISession, if you use ORM like NHibernate, and returns a list of IData objects.

 public interface IQuery<T> { IEnumerable<T> DoQuery(IUnitOfWork unitOfWork); } 

Thus, you can create strongly typed request objects that meet your requirements and have a clean interface for your services. This article from Ayende gives a good idea of ​​the subject.

+3
source

It sounds to me like you need a different interface, so the method becomes something like:

 public List<IData> GetData(IDataRequest request) 
+1
source

You are delegating a different layer, and this is not necessary.

You could add some other logic here or in another method on a line that belongs only at this level, or change it to link the layer to another implementation, so this can certainly be a very good use of layers in issue.

You may have too many layers, but I wouldn’t say so simply when I saw this, without seeing anything else.

0
source

From what you described, it just looks like you came across one of the “trade-offs” of abstraction in your application.

Consider the case when these “call chains” no longer “skip” data, but require some transformation. It may not be needed now, and, of course, a deal could be done for YAGNI .

However, in this case there is not too much technical debt to handle a positive side effect, making it easy to make changes to data between layers.

0
source

I am using this template. However, I used it to remove the objects of my domain model from my data objects.

In my case, instead of "passing" an object coming from the data layer, as in your example, I "map" it to another object that lives in my domain layer. I use AutoMapper to get rid of the pain made by hand.

In most cases, my domain object looks exactly like my data object from which it came from. However, there are times when I need to smooth out the information coming from my data object ... or I may not be interested in everything that is in my data object, etc. I map a data object to a configured domain object that contains only the fields my domain layer is interested in.

It also has a side effect when I decide to change the factor or change my data level for something else. It should not affect my domain objects, since they are de-connected using the mapping method.

Here is a description of auto-mapper that is kind of trying to create this design template, I think:

AutoMapper is focused on model projection scenarios to smooth complex object models for DTOs and other simple objects, the design of which is better suited for serialization, messaging, messaging, or just an anti-corruption layer between the domain and the application level

0
source

Actually, the way you decide to go is the reason that you have (I'm not saying that this is bad).

First, let me say that your approach is quite normal.

Now let me think about your layers:

  • Your service - provides several types of strictly typed access models. This means that it has some types of arguments used in some special types of methods, which again return some particular type of results.
  • The level of access to the service also provides the same model. So it takes special arguments for special kinds of methods, returning special kinds of results.
  • etc...

In order not to be confused, this is what I call a special kind:

 public UserEntity GetUserByID(int userEntityID); 

In this example, you need to pass exactly Int, while GetUserByID is called, and it will definitely return a UserEntity object.

Now a different approach :

Remember how SqlDataReader works? not very strongly typed, is it? In my opinion, you call this the fact that you are missing some kind of not strictly typed layer.

For this to happen: you need to switch from strongly typed to not strongly typed somewhere in your layers.

Example :

 public Entity SelectByID(IEntityID id); public Entity SelectAll(); 

So, if you have something like this instead of the level of access to the service, you can call it for any arguments that you wanted.

But this almost creates its own ORM, so I don't think this is the best way to go.

0
source

It is necessary to determine what responsibility belongs to that layer, and place such logic only in that layer to which it belongs.

It’s absolutely normal to just go through if you don’t need to add any logic to a particular method. At some point, you may need to do this, and the level of abstraction pays off at that moment.

It’s even better to have parallel hierarchies rather than just passing bottom-layer objects up, so each layer uses its own class hierarchy, and you can use something like AutoMapper if you feel that there isn’t much difference in the hierarchies. This gives you flexibility, and you can always replace automation with custom mapping code in certain methods / classes if the hierarchies no longer match.

If you have many methods with almost the same signature, you should consider a query specification template.

 IData GetData(IQuery<IData> query) 

Then at the presentation level, you can implement data binding to your custom request specification objects, where one aspnet handler can implement the creation of specific request objects and pass them to one service method, which will transfer it to one repository method, where it can be sent according to a specific a query class, possibly with a visitor pattern.

 IQuery<IData> BindRequest(IHttpRequest request) 

With this Automapping and Query Specification template, you can reduce duplication to a minimum.

0
source

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


All Articles