Showing posts with label Design pattern. Show all posts
Showing posts with label Design pattern. Show all posts

Monday, July 16, 2018

C# Logging: Where To Setup The Logger

Applications need logging. In C#, you will typically log various types of events like exceptions, debug, and informational. Your logs will contain valuable information which you'll use to tune your application and understand user behavior.

It's easiest to either pass an enum value to a single Log method or call a specific method to write different types of events. It's typical to write to different logs depending on the context. The problem is, where and how do we access the logging API from our methods?

Dependency Injection


Dependency Injection is one possible approach. If you go this route, you have to pass around a logger to all your classes as a dependency. You can use constructor injection and take the logger in as a constructor parameter. For a User class, you would pass it in along with the UserRepository as follows:


public class User
{
  private readonly IRepository<UserData> _userRepo;
  private readonly ILogger _logger;

  public User(IRepository<UserData> userRepo, ILogger logger)
  {
    _userRepo = userRepo;
    _logger = logger;
  }
}


The User class takes in the logger along with any other dependencies.

Via IoC Container

Even if you use an IoC container like Ninject or Unity, you have to add the logging interface to all the constructors and make a class member. With the IoC container approach, you can also ask the container for the implementation.

Container.Get<ILogger>()?.LogError(...);

Results and specific approach may vary according to the container, but this will at least allow you to fetch the logger whenever you need it without having to pass it around.

The .NET Core Way

In .NET Core 2.1, there are two ways to go about it as written by Luke Latham. Luke recommends using the LoggerMessage class over the LoggerExtensions for high-performance applications.

The Problem With DI

But let's suppose that you're not using core and you don't like the idea of adding the logger dependency EVERYWHERE! It isn't a dependency of the class that has anything to do with the business logic of the class. It's an implied dependency of all classes in the application. If you first design the class without the logging dependency, then you have to add it when you need to use logging.

IoC containers make this easier, but what if you have static methods from which you need to log (it happens)? In that case, you don't have constructor variables. You could pass the logger in the method params, but then you're passing around a logger. That's not a particularly elegant pattern! For one thing, you end up with more parameters. Another, you get into the habit of passing things around you may or may not need.

As Global

If it's a dependency everywhere, it's a global dependency. I don't like globals for many things. They are for configuration and that's really about it. It's beneficial to have a global logger too!

I also don't recommend statics for most things. Well, the logger could be a static global so long as it doesn't have mutable state. In other words, so long as you set the logging configuration once and leave it along throughout the entire application lifetime.

Log.LogError(...);

And in your implementation of the static method, you can get it from the container or some other global context.


public static void LogError(params object[] logParams)
{
  Container.Get<ILogger>()?.LogError(logParams);
}

This is actually a better practice since you only have to change your Log class if you change IoC containers and the interface doesn't match. It's better separation of concerns (SOC). The place to change the logging is in the Log class rather than everywhere else!

Concluding


Most of the methods discussed here are fine and dandy. I'm just not a huge of fan of having to pass things around in every class. I wouldn't extend this idea to something like a repository though, those are business logic specific dependencies that actually have meaning to the class at hand. A User class taking a UserRepository is meaningful. Every class taking an ILogger has no meaning and actually distracts from the purpose of the classes.

In sum, global is the place for the logger!

Tuesday, September 6, 2016

Coordinator Pattern for Distributed Enterprise Applications

When building software for the enterprise, particularly where data access to one or more source will be shared across multiple application domains, and where distribution and reuse through distribution is a virtue, it would be helpful to use a Coordinator as the authority for the unit of work. A Coordinator is a service which accepts a single incoming command and issues one or many commands to resources (data access services). The resources are separated into read and write resources for each source. The read resources may be used by the presentation layer or the Coordination Layer, but the write layer (which specializes in executing commands) may only be used by the Coordination Layer.
A Coordinator is similar to a Unit of Work pattern. The key difference is that it must be hosted as a service and use data access as services.
The Coordinator owns the transaction. It must begin and either rollback or commit the transaction. The transaction may be distributed across many data sources (throughout the writer resources). The writer resources must be designed in a way that handles concurrency. For example, if using an ORM you may need to load then update or insert any specific data entities involved in the transaction. These writer resources should handle the entire transaction for the data source which it wraps (typically one database). The Coordinator should send commands to any resources and raise any events necessary upon executing the commands.


The Coordinator should listen for events (as in event based architecture) and respond to those events by issuing those specific commands to the resources which it consumes. Those resources may be shared amongst other Coordinators. Resource commands should be synchronous so that transactions can be used appropriately. The Coordinator is an architectural pattern which can be used to implement a Unit of Work pattern in a distributed, service-based architecture. Coordinators should be consumed asynchronously using event-driven mechanisms. Coordinators should respond to events raised by other Coordinators or by users via the presentation layer.


Through the use of Coordinators in conjunction with the other patterns expressed in this article, a distributed system can be built such that it is loosely-coupled, highly-cohesive and easily-maintainable. The system will be loosely-coupled in that the communication between Coordinators occurs via events. Zero, one, or many Coordinators can respond to an event. Any Coordinator can be changed and (if hosted independently) redeployed without impacting any other Coordinator. The level of cohesiveness depends on the scope built into each Coordinator. Each Coordinator could handle one Unit Of Work. In this case each one would have a single service method exposed. This method may perhaps share a contract and differ only by location. In this case the method definition would need to describe an event and the Coordinator would need to load the Event details it needs via resources. In this case, an Event Store would be needed. Else, the payload would be a serialized packet of all of the data relevant to the event. This is a deeper topic and will require more in-depth exploration in order to weigh in completely on the interface mechanisms involved. The maintainability is affected by the number of moving pieces, but also by having a standard mechanism of control over the various state changes involved in a complex system.