Using app-level settings in rich domain models
Asked Answered
B

1

1

I want to have a global/app level setting that I want to use in my rich domain models.

I have a method that does some calculations based on the argument passed.

This might not be the best example and might not look like a method belonging to the domain model but I try to keep it simple so you could understand the problem.

public decimal Calculate(CalculationMethod calculationMethod)
{
    switch (calculationMethod)
    {
        case CalculationMethod.Multiply: 
            return _x * _y; // _x and _y are fields
        case CalculationMethod.Divide: 
            return _x / _y;
    }
}

Now let's say I have many methods like this in my other domain models that also accept CalculationMethod in their methods. I would like to have a global setting so I can set a calculation method globally so it could be used by all methods that take it as a parameter.

One solution would be to read the configuration every time I call this method.

I wonder if there's any better way so I can set CalculationMethod globally and never pass it and instead, having some kind of static variable(singleton) that holds the calculation method and reading it directly inside my methods without passing it. But I think there would be a problem of thread-safety then.

public decimal Calculate()
{
    // or read it from file here?
    switch (GlobalSettings.CalculationMethod)
    {
        case CalculationMethod.Multiply: 
            return _x * _y; // _x and _y are fields
        case CalculationMethod.Divide: 
            return _x / _y;
    }
}

I can't pass it in the constructor because it's not something that belongs to my domain model.

How to approach this kind of problem? Is there any way better than the two I mentioned?

I asked this question in the comment under Mark Seemann's answer here: App-level settings in DDD?

Beersheba answered 14/9, 2018 at 8:2 Comment(7)
Singleton pattern? csharpindepth.com/Articles/General/Singleton.aspxHenrie
You can read settings once at start of application and than inject them with IoC container wherever you need. Through constructor injection, for example. Btw, correct implemetnation of singleton is thread safe.Henrie
@Henrie I don't want to inject anything in my domain models. They're just data holders with a logic that is performed only on that data.Beersheba
Inject into service layer classes and then pass as a parameter, just as you do in first example. Or use Singleton.Henrie
Yeah, those are the optionsBeersheba
But I think there would be a problem of thread-safety then. - if you just read then there is no concurrency problemCrabstick
switch (calculationMethod) - couldn't this switch be refactored to something abstract, like an interface?Crabstick
R
2

As Clean Code explains, passing flags to methods is generally considered sub-optimal design. I understand that the OP is a stand-in for another, more complicated problem, but I'd be inclined to recommend to refactor to a polymorphic object model:

public interface ICalculator
{
    decimal Calculate();
}

You can now define implementations as required:

public class Multiplier : ICalculator
{
    public decimal Calculate()
    {
        return _x * _y; // _x and _y are fields
    }
}

public class Divider : ICalculator
{
    public decimal Calculate()
    {
        return _x / _y;
    }
}

You can inject an ICalculator object into any class that has a need for it, using Constructor Injection. In your Composition Root, you can read a configuration file, or in some other way decide which implementation to use, and then create only a single instance of that class. This gives the object Singleton lifetime, so that the calculation method is entirely selected at startup time, and shared among all objects in the application.

Rearward answered 14/9, 2018 at 10:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.