IoC Demystified (Part 1 of 2)
Today's topic will be IoC demystified. IoC is short for inversion-of-control, an idea coining back to the late 1980s and becoming popularized in the mid 2000s with statically typed languages, such as Java and C#.
The primary concept with inversion-of-control, at least in my own words, is the idea of inverting the flow of object dependencies, so that we can manage and control them. One of the most common approaches of accomplishing inversion-of-control is through a technique called dependency injection, which is where an object is injected with the objects it directly depends on.
There are at least three forms of dependency injection. One of the most common forms that we'll be using in this article is constructor injection. Constructor injection is the mechanism of directly injecting dependencies as constructor parameters though an object's constructor.
Example: Injecting the ServiceDependency object into the Service object.
public class Service
{
private readonly ServiceDependency _serviceDependency;
public Service(ServiceDependency serviceDependency)
{
_serviceDependency = serviceDependency;
}
}
The Tutorial
Before we begin, the following tutorials will be written in C# and to be able to follow along, I will assume you have a fairly strong understanding of both C# and .NET. The code will be written to work with .NET 5.0.
To start, we'll be building a simple C# console application that simulates using the latest advancements in design principles and patterns to create a blog post posting subsystem. Let's get started!
High-level architecture
One of the common approaches taken is to break apart our logic into several services. Services will have their functionality defined via high-level service interfaces, as to decouple and abstract away implementation details to service interfaces. Each service interface will be mapped to system implementations via containers, which will be used to resolve these service implementations via their interfaces.
Interface
Interfaces provide a definition (or contract) defining what features a specific system service provides
Service
Services, defined by an interface, provides a single responsibility of purpose to the overall software system ecosystem
Container
A container is a mapping between interfaces and services that will be used to to resolve a graph of dependencies when a system interface is requested
Building the interfaces
I envision creating the following three separate interfaces:
IBlogPostRepository: A repository pattern to persist the blog posts somewhere
public interface IBlogPostRepository
{
void CreateBlogPost(string title, string createdBy, string blogContent);
}
INotificationService: A service pattern to push notifications to our fellow subscribers
public interface INotificationService
{
void SendNotification(string message);
}
IBlogPostService: A service pattern to encapsulate creating blog posts and notifying subscribers of new blog activity
public interface IBlogPostService
{
void CreateBlogPost(string title, string createdBy, string blogContent);
}
Building the Services
BlogPostRepository
public class BlogPostRepository : IBlogPostRepository
{
public void CreateBlogPost(string title, string createdBy, string blogContent)
{
Console.WriteLine($"Blog post: '{title}' by {createdBy} successfully saved to disk");
}
}
NotificationService
public class NotificationService : INotificationService
{
public void SendNotification(string message)
{
var notification = new StringBuilder();
notification.AppendLine("Sending Message:");
notification.AppendLine(message);
Console.WriteLine(notification.ToString());
}
}
BlogPostService
public class BlogPostService : IBlogPostService
{
private readonly IBlogPostRepository _blogPostRepository;
private readonly INotificationService _notificationService;
public BlogPostService(
IBlogPostRepository blogPostRepository,
INotificationService notificationService)
{
_blogPostRepository = blogPostRepository;
_notificationService = notificationService;
}
public void CreateBlogPost(string title, string createdBy, string blogContent)
{
_blogPostRepository.CreateBlogPost(title, createdBy, blogContent);
var message = new StringBuilder();
message.AppendLine("Hi subscribers!");
message.AppendLine($" {createdBy} has just uploaded a new blog post, '{title}'.");
message.AppendLine(" Check it out and be sure to add a thumbs up!");
message.AppendLine("Thanks, The Coding Bruises staff");
_notificationService.SendNotification(message.ToString());
}
}
At this point, we'll continue into building a simple dependency injection container in part 2 of this 2 part series. Stay tuned!