design-patternsdependency-injectionarchitecturerefactoringcoupling

Is this class avoiding temporal coupling


I have a wrapper object that takes a connection string and opens up a database and initializes an API before it can be used. After reading Mark Seemann's blog post on temporal coupling I've been going crazy over trying to refactor my API so it is clear. I've looked at the .NET class SqlConnection and they require Open before you use it, so maybe this type of coupling is unavoidable? I don't know if there is a way to do what I want since it's a wrapper of an existing API but here is what I have. There can only be one connection at a time, else you have to close and restart

public class MyService
{
    public MyService(string dataSource)

    public void StopRequest()

    public void StopAPI()

    public Response SendRequest(string request, string pass)

}

You see it requires a data source string, and starts the API so SendRequests will work but you can only really have one service at a time so unless I save it in some sort of static var or DI singleton, you'd have to call new ..(datasource) every time even though it's always the same. The only thing that bothers me is if you call StopRequest or StopAPI & then call SendRequest, it will throw an error of course. I think this is avoiding temporal coupling but wanted some thoughts.


Solution

  • Temporal Coupling is a design smell, which is not always avoidable. Especially when dealing with already existing APIs or sometimes external services that need operations to happen in a certain (temporarily coupled) order. SqlConnection is a good example, because it requires you to call Open, which is implicit and something I still forget to do now and then. On the other hand, letting the SqlConnection open the connection from inside its constructor is a bad idea as well, which is the reason for the existence of the Open method in the first place.

    But in most cases you can hide complexity of temporal coupled APIs behind an abstraction and have that complexity solely implemented inside an adapter implementation. This prevents the rest of the application from having to deal with this temporal coupling.

    It's hard to give very specific feedback on your use case, because some details are missing. But I can imagine the StopAPI to not be part of the abstraction and called from inside a Dispose method on your adapter.

    If the order of interaction is crucial and impossible to abstract, you can prevent temporal coupling by letting methods return instances that need to be passed on to a next method in order to operate. This makes the dependency between methods very explicit, compiler verified, and therefore removes the temporal coupling. For instance:

    public class MyService
    {
        public MyService(string dataSource);
    
        public Api StartApi();
    }
    
    public class Api : IDisposable
    {
        public Request SendRequest(string request, string pass);
    
        public void Dispose(); // Calls StopAPI internally
    }
    
    public class Request : IDisposable
    {
        public Response Response { get; }
    
        public void Dispose(); // Calls StopRequest internally
    }