Should AggregateService be used as a container for storing common dependencies?

I have a base controller class that uses aggregated services in autofac to inject its dependencies. In the class obtained from the base controller, I need to access the service, which is part of the aggregate container. Which one is better?

public Interface ICommonControllerDependencies { ICookieService CookieService{ get; } IAuthenticationService AuthenticationService{ get; } } public abstract class BaseController : Controller { protected readonly ICommonControllerDependencies Dependencies; protected BaseController(ICommonControllerDependencies dependencies) { this.Dependencies = dependencies; } protected override void OnActionExecuting(ActionExecutingContext filterContext) { filterContext.Controller.ViewData[UserName] = Dependencies.AuthenticationService.UserName; base.OnActionExecuting(filterContext); } protected ActionResult RedirectToDefaultPage() { var page = Dependencies.CookieService.GetDefaultPageCookie(Request, RouteData); //Do Something } } public class BaseReportController : BaseController { public BaseReportController(ICommonControllerDependencies dependencies) : base(dependencies) { _cookieService = dependencies.CookieService; } } 

OR

 public class BaseReportController : BaseController { public BaseReportController( ICookieService cookieService, ICommonControllerDependencies dependencies) : base(dependencies) { _cookieService = cookieService; } } 
+4
source share
3 answers

AggregateService designed to isolate your subclass from changes in the superclass and enable changes in the superclass without breaking subclasses. If you go to option 1, you attach the subclass to your superclass, thereby losing the intended isolation.

Without knowing more about your design, I would go for option 2. I do not think that I can expect ServiceA always be there. By accepting an explicit dependency on IServiceA in a subclass, this class can live happily unaware of changes occurring in the inside of the superclass.

+2
source

If you need an aggregation service (as the Autofac documentation calls it), this may be a sign of a violation of the Single Responsibility Principle , which means that a class must do one thing and only one.

In other words, the aggregate service pattern is the smell of code *.

Having large base classes is the smell of code , because base classes tend to grow and evolve into large and complex classes that contain a lot of code that not all subtypes use. Instead, the general advice is to composition over inheritance .

However, with user interface interfaces such as ASP.NET MVC, this is not always easy, since most frameworks themselves contribute to inheritance.

Try to extract the logic of the base class to separate the dependencies, especially if this code in the base class is not used by all subtypes. For example, you can reorganize the RedirectToDefaultPage method to the following:

 public class DefaultPageRedirector { private readonly ICookieService cookieService; public DefaultPageRedirector(ICookieService cookieService) { this.cookieService = cookieService; } public ActionResult RedirectToDefaultPage( Controller controller) { var page = this.cookieService.GetDefaultPageCookie( controller.Request, controller.RouteData); //Do Something } } 

This way you can only enter DefaultPageRedirector into the types of Controller that really need it.

For OnActionExecuting it is different, as it is called for each subtype. However, the ViewData["UserName"] property will probably not be used by every View on the system, in which case you should consider returning the UserName as part of the (statically typed) ViewModel object. If it is used by most views, you might consider using a partial view because you might have some repeating code in your views (the DRY principle holds not only for code, but for every part of the system).

This will probably save most of the code from the base class, which will probably also remove most of the dependencies in the database (if not all).

* Please note that the smell of code does not mean that there is always a problem. To quote Wikipedia: "The smell of code is any symptom in the source code of a program that may indicate a deeper problem."

+1
source

I would think that it depends on whether you have IserviceA registered in the container or not, and how you expect your design to evolve.

Personally, I would go with the first option if you do not expect to remove serviceA from the ICommonControllerDependencies interface. It’s just easier to enter one interface. If you think you can remove serviceA at some point, I would consider the second option, as it removes the connection between the service and the controller dependencies in BaseReportController. I think the second option more closely follows the law of the Law of Demeter .

0
source

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


All Articles