DEV Community

Cover image for Clean Architecture Essentials

Clean Architecture Essentials

Ivan Paulovich on November 14, 2019

The "Software Architecture" We usually see Software Architecture descriptions like "The software architecture is an ASP.NET Web API with...
Collapse
 
darianbenito profile image
Darian Benito • Edited

Hi Ivan! I have a new question: What about if we need to consume an external api ? We need to create an interface IExternalService in the Application proyect, but where is the place to implement this interface? Infrastructure ? Thanks!

Collapse
 
ivanpaulovich profile image
Ivan Paulovich

Hi @darianbenito ,

I had to take a long break from laptops last weeks and coming back to routine slowly. Let's see if I could clarify you:

In the Domain you design your Model putting your business logic in priority. Suppose your application gives you suggestions of what to do based on the local weather.

namespace Domain
{
    using System;

    public interface IWeather
    {
        decimal Temperature { get; }
        string GetDailySuggestions();
    }

   public interface IWeatherGateway
   {
        Task<IWeather> GetWeather();
    }

    public abstract class Weather : IWeather
    {
        public abstract decimal Temperature { get; }

        public string GetDailySuggestions()
        {

            //
            // The business logic need to know the temperature outside then you
            // write your code against the interface
           //

            if (this.Temperature < 10)
            {
                return "Stay at home. Watch Netflix.."
            }
            else
            {
                return "Go walk in the forest."
            }
        }
    }
}

Later you write a weather provider:

namespace Infrastructure.TheWeatherChannelProvider
{
    using System;

    public sealed class AccuWeather : Domain.Weather
    {
        public Customer(decimal temperature)
        {
        }

        public override decimal Temperature { get; }
    }

    public sealed class AccuWeatherGateway : Domain.IWeatherGateway
    {
        private readonly _accuWeatherUrl;

        public UserRepository(string accuWeatherUrl)
        {
            this._accuWeatherUrl = _accuWeatherUrl;
        }

        public async Task<IWeather> GetWeather()
        {
            //
            // HTTP client code ommited.
            //

            decimal localTemperature = 42;

            return new Weather(localTemperature);
        }
    }
}

Thanks again for reaching me out, I really appreciate feedback.
I hope this would give you insights.

Best,
Ivan

Collapse
 
darianbenito profile image
Darian Benito

Thanks Ivan!

Collapse
 
darianbenito profile image
Darian Benito

Hi Ivan, Thanks for share.

What about if we need to obtain all registers from a entity (GetAllEntityXBySomeProperty) to load a DropDownList ? We need to create a UseCase for it, right? Or we just call to the EntityXRepository (GetAllEntityXBySomeProperty method) from the UI ...
Let me shared a personal code:

public class GetAllPropertyTypesByPropertyAgencyUseCase : 
     IGetAllPropertyTypesByPropertyAgencyUseCase
        {
          private readonly IPropertyTypeRepository _propertyTypeRepository;

          public GetAllPropertyTypesByPropertyAgencyUseCase(IPropertyTypeRepository 
          propertyTypeRepository)
          {
             _propertyTypeRepository = propertyTypeRepository;
          } 

           public void Execute(GetAllPropertyTypesByPropertyAgencyUseCaseRequest useCaseRequest, 
           IOutputPort<GetAllPropertyTypesByPropertyAgencyUseCaseResponse> outputPort)
            {
              IEnumerable<PropertyType> propertyTypes = 
              _propertyTypeRepository.GetAllByPropertyAgency(useCaseRequest.PropertyAgency);

               if (propertyTypes == default(IEnumerable<PropertyType>))
                 {
                  outputPort.HandleError(new List<UseCaseError> { new UseCaseError(1, $" 
                  {nameof(_propertyTypeRepository.GetAllByPropertyAgency)} Not Found") });
                  }

                 var getAllByPropertyAgencyPropertyTypeUseCaseResponse = new 
                GetAllPropertyTypesByPropertyAgencyUseCaseResponse(propertyTypes);

                 outputPort.HandleSuccess(getAllByPropertyAgencyPropertyTypeUseCaseResponse);
     }
} 
Collapse
 
ivanpaulovich profile image
Ivan Paulovich

Hey Darian,

Yes, for each interaction you need to create an use case. I implemented an use case similar to yours on Manga Project github.com/ivanpaulovich/clean-arc.... Checkout the branch "React" for the most up to date code.

BTW your use case implementation is missing a return after outputPort.HandleError. Let me know if you have more questions :)

Collapse
 
eladchen profile image
Elad Chen • Edited

So.. output ports implementations (such as CloseAccountPresenter below) are aware of the domain layer / objects that reside two layers down?

CloseAccountUseCase.cs#L75-L77
CloseAccountPresenter.cs#L27-L28
CloseAccountOutput.cs#L25

I was under the impression that a layer should only
be dependent on the next layer?

Collapse
 
ivanpaulovich profile image
Ivan Paulovich

Hi @eladchen ,

Thanks for the input. On my design a prioritized that "when we pass data across a boundary, it is always in the form that is most convenient for the inner circle." blog.cleancoder.com/uncle-bob/2012...

Considering this I pass domain objects to the Presenter methods as arguments then it is responsible to generate the visualization for each case. I did differently in the past using concrete output objects in the application layer but the conversions didn't payout in the end.

I need to empathize that I follow the general rule to not to expose Domain objects on REST APIs.

Collapse
 
eladchen profile image
Elad Chen

Awesome - This has been a real pickle, I'm glad to know that didn't pay out.

Collapse
 
geofflangenderfer profile image
Geoff Langenderfer

High value post, thank you. This is the high-level thinking sorely missed by most junior devs.

Collapse
 
ivanpaulovich profile image
Ivan Paulovich

I appreciate the feedback 🍺! My goal is to make this content accessible to them.

Collapse
 
arjunan profile image
sarjunan

Thank you so much for sharing this article.one day I would like to write a clean code like you

Collapse
 
ivanpaulovich profile image
Ivan Paulovich

Thank you @sarjunan! There is no secret, study the Adaptive Code and the Clean Architecture books then practice it on your projects a little bit every day.
Bring your questions to me on GitHub or Twitter :)

Collapse
 
kobbidev profile image
Kobbi

Awesome post about Clean Architecture. Thank you so much.

I did not understand the utility of presenter class. I tought that the presenter class should wrap up multiple return types using generics. It so, it would map the use case output to the specific UI that requested the use case execution.

In your example, using the automapper would't do the job?