Playing with Dependency injection in a Console app?
Tags: csharp, programming
Published: 2021-06-26 13:43:54

A few days ago I was asked to create some console app that lets you ask some question around weather. Its a pretty much standard easy peasy console app you can wip out in an hour or even half if you're feeling it and is on zone.

But its boring and bland, so I decided to play around with overkilling it with a controversial pattern in recent years, Dependency Injection!! cause why not?

So what's Dependency Injection anyway?
Uhmm its fairly nerdy but as a basketball fan too I have original way of explaining it (feel free to attack me if its plain wrong lol)

Imagine you go to the gym to do some shooting, Normally you go shoot the ball, rebound your own ball or even chase the ball, then go back to your position, finally able to shoot and repeat the process again. 

Now in dependency injection, all you have to do really is just shoot the ball cause when you need the ball it will be passed to you automatically. 


And compiler now is seems really smart about it....

{% twitter 1085818038916730880%}


Well enough of that talk let's get jump into it.

First thing first is making sure I have setup the dependency injection in the console app. For that I need the `Microsoft.Extensions.DependencyInjection`

and it will have the following setup code in here
```csharp
// Build configuration
configuration = new ConfigurationBuilder()
    .SetBasePath(Directory.GetParent(AppContext.BaseDirectory).FullName)
    .AddJsonFile("appsettings.json", false)
    .Build();

// Boostrapping the app
var serviceProvider = new ServiceCollection()
    .AddSingleton(configuration)
    .AddSingleton<IWeatherstackService, WeatherstackService>()
    .AddSingleton<IQuestionService, QuestionService>()
    .AddSingleton<IUserInteractionService, UserInteractionService>()
    .BuildServiceProvider();


var weatherApp = serviceProvider.GetService<IUserInteractionService>();
// Start the App
weatherApp.Start();

```

My acceptance criteria is to get user's zip code then answer few predefined questions that user can select. These questions are mostly related to weather so for this mini project we will use WeatherStack API.

Okay gameplan coach, `WeatherStackService` will handle the http request to WeatherStack. I've used `Flurl` as a http request client because of sugar syntax it provides as well as simplicity. 

`QuestionService` will handle the pre-defined questions, as well as validation or the actual answering of the question. So let's say its a question if "Is it raining?" some sort of that, then this service will try to answer that based on info we will get from `WeatherService`.

Then these two will be injected to `UserInteractionService`, this service will handle the grunt work. It will pretty much will look like this 

```csharp
private readonly IQuestionService _questionService;
private readonly IWeatherstackService _weatherstackService;
public UserInteractionService(IWeatherstackService weatherstackService,
    IQuestionService questionService)
{
    _weatherstackService = weatherstackService;
    _questionService = questionService;
}
```

 Now we are ~~passing~~ ehem injecting the two services into `UserInteractionService`

![Pass it](https://media.giphy.com/media/7JZQd4ftvufaOsnD6y/giphy-downsized-large.gif)

Now this where it gets interesting, we have both of our dependent injected and ready to use. We can easily visualize it this way
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qahu441ev9tik2l01i3a.png)
 
With DI, the main thing in order for this pattern to succeed is planning ahead, listing the services and visualizing how each of this will relates to each other is the key. Cause just slapping the service into each other will make the code messy and complexity arises exponentially. You can use something like `graphviz` or `PlantUML` to visualize each of your components.

Now to continue, Let's say the user already added the zip code, We will ask the WeatherStackService to get the weather info and store into a POCO.
```csharp
private WeatherStackResponse AskZipCode()
{
....
....
    var task = Task.Run(async () => await _weatherstackService.GetWeatherInfo(zipCode));
    var result = task.Result;
    if (result?.location != null)
    {
        Console.WriteLine(string.Format(Constants.ZipCodeValid,
            result?.location.name, result?.location.country, result?.location.region));
        return result;
    }else{
     ....
    }

}
```

Faily cool right? we don't need to set it up or even instantiate it. One of good things with DI Pattern is you can isolate things up more properly, which in turns also make the code more testable.
Cause instead of worrying about factory methods, constructors, you can just keep things much more simple. Though like anything else overused and abused it can be detrimental too.

Now back at it, we can finally at the last solve the problem which is answering a question

```csharp
private void AskQuestion(WeatherStackResponse weatherInfo)
{
    Console.ForegroundColor = ConsoleColor.Green;
    Console.WriteLine(Constants.SelectQuestionMessage);
    var questions = _questionService.GetQuestions();
    var selectedQuestion = UIUtils.PrintQuestion(questions);

    // Generate Answer
    var result = _questionService.AnswerQuestion(selectedQuestion, weatherInfo);
....
....
}
```
Anyway here how it looks like.

## Demo
![Demo](https://media.giphy.com/media/mgLB8OPIrsPJgbwWEP/giphy.gif)


## Afterthought
Now one thing I want to point out before ending this ramble of mine, is DI is pretty cool if it used correctly. 
1. DRY can be easily enforced. Hail Decoupling responsibilities!
2. a much testable isolated code and many more.
3. Its somewhat fairly maintainable? (subjective, for me yes)

But as great as it is, it's not for everyone.
1. You need to write more interfaces, classes etc.
2. It increases the complexity, mostly if you don't visualize the dependency tree beforehand and just slap the services to each other.
3. And a small price to pay for overhead performance. (With machines nowadays, I reckon its noticeable enough but add this for the .ms seeksers)


You can look at the final code [here](https://github.com/klawingco/weather-assistant)


Anyway, I hope you find this entertaining, Its been years since I put my pen or rather keyboard to write something, so I am somewhat rusty but I totally had fun. Have a nice day!