I am trying to wrap my head around the concepts of Clean Architecture, and specifically about the responsibility of the Infrastructure layer. Its purpose is often described as "to provide functionality to access external systems", which is a bit vague when you try to think deeply about it.
Consider this example. Let's say I'm developing an app that determines if the weather tomorrow is going to be good or bad. In order to do that, it tries to call 2 weather forecast providers via REST APIs: first it calls the main provider, and if their service is down, it calls the backup provider.
My current project structure is the following:
/Application
IDataProvider // interface for weather forecast provider
bool IsWeatherGood(Date date); // just definition, no implementation
WeatherService // Main application logic
IDataProvider[] providers; // Injected via DI
bool IsWeatherGood(Date date) {
foreach (var provider in providers)
try { return provider.IsWeatherGood(date); } catch {}
throw Error("None of the providers worked")
}
/Infrastructure
/WeatherForeacstProviders
/ProviderOne
/DataContracts
ProviderOneApiRequest // API contract
ProviderOneApiResponse // API contract
/ApiClientImpl
ProviderOneApiClient : IDataProvider
bool IsWeatherGood(Date date) {
var response = callApi("http://provider1.com/get-weather");
if(response.IsSunny) return true;
return false;
}
/ProviderTwo
/DataContracts
ProviderTwoApiRequest // API contract
ProviderTwoApiResponse // API contract
/ApiClientImpl
ProviderTwoApiClient : IDataProvider
bool IsWeatherGood(Date date) {
var response = callApi("http://provider2.com/forecast");
if(response.SunshineLevel > 10) return true;
return false;
}
My concern is that the infrastructure level contains part of the business logic: it converts API responses into the domain concept of "good/bad weather". Is this a proper responsibility for the infrastructure layer? Currently, our domain experts consider ApiTwo's sunshine level above 10 as "good". But this threshold may change in the future, and it it does, we'd need to make changes in the infrastructure level, which doesn't sound right, because this threshold looks more like business logic.
Another option would be to move this logic into the Application layer. But that would mean that the application layer would be aware of the 3rd-party APIs' data contracts, which doesn't sound right either according to the definition of the Infrastructure layer. Also, in this case the infrastructure layer would only be a thin abstraction over HTTP calls with one or two methods like PerformPostRequest(obj body)
, which is redundant in many modern programming languages / frameworks, as the frameworks themselves often provide such abstractions.
Which approach (if any) is "more correct" in the context of Clean Architecture?
response.SunshineLevel
into the domain concept of "good/bad". One API may provideSunshineLevel
in the response, another may provideCloudLevel
, etc. And those various responses need to be converted intobool IsWeatherGood
according to some rules (egSunshineLevel > 10
,CloudLevel == "Low"
, etc). Would such conversion be considered business logic that should not live in the Infrastructure Layer?CloudLevel
andSunshineLevel
into bool or whatever the application needs. If rules are not that simple and depend on a complex algorithm that only the application knows, then this "knowledge" is injected into the port. As objects (OOP approach) or as functions (functional approach). Ask yourself. What decides what good weather is? is my application? is my provider? Who dictates this? And Who could change this? The business experts? Provider's semantics or data?