There\'s this framework that I\'m helping to design. There are some common tasks
ID: 643203 • Letter: T
Question
There's this framework that I'm helping to design. There are some common tasks that should be done using some common components : Logging, Caching and raising events in particular.
I am not sure if it's better to use dependency injection and introduce all of these components to each service (as properties for example) or should I have some kind of meta data placed over each method of my services and use interception to do these common tasks?
Here's an example of both:
Injection:
public class MyService
{
public ILoggingService Logger{get;set;}
public IEventBroker EventBroker{get;set;}
public ICacheService Cache{get;set;}
public void DoSomething()
{
Logger.Log(myMessage);
EventBroker.Publish<EventType>();
Cache.Add(myObject);
}
}
and here's the other version:
Interception:
public class MyService
{
[Log("My message")]
[PublishEvent(typeof(EventType))]
public void DoSomething()
{
}
}
Here are my questions:
Which solution is best for a complicated framework?
If interception
wins, what are my options to interact with internal values of a method (to use with cache service for example?)? can I use other ways rather than attributes to implement this behavior?
Or maybe there can be other solutions to solve the problem?
Explanation / Answer
Cross-cutting Concerns like logging, caching etc. are not dependencies, so shouldn't be injected into services. However, while most people then seem to reach for a full interleaving AOP framework, there's a nice design pattern for this: Decorator.
In the above example, let MyService implement the IMyService interface:
public interface IMyService
{
void DoSomething();
}
public class MyService : IMyService
{
public void DoSomething()
{
// Implementation goes here...
}
}
This keeps the MyService class completely free of Cross-cutting Concerns, thus following the Single Responsibility Principle (SRP).
To apply logging, you can add a logging Decorator:
public class MyLogger : IMyService
{
private readonly IMyService myService;
private readonly ILoggingService logger;
public MyLogger(IMyService myService, ILoggingService logger)
{
this.myService = myService;
this.logger = logger;
}
public void DoSomething()
{
this.myService.DoSomething();
this.logger.Log("something");
}
}
You can implement caching, metering, eventing, etc. in the same way. Each Decorator does exactly one thing, so they also follow the SRP, and you can compose them in arbitrarily complex ways. E.g.
var service = new MyLogger(
new LoggingService(),
new CachingService(
new Cache(),
new MyService());
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.