Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Javier Val - Task #675

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open

Javier Val - Task #675

wants to merge 6 commits into from

Conversation

jvl83
Copy link

@jvl83 jvl83 commented Jan 16, 2025

ExchangeRateProvider

Intro

This project illustrates an example of API implementation, taking into account good design practices, Clean Architecture DDD and adhering to the CQRS principles. It could be considered a standalone microservice and deployed and scaled independently.

This exercise aims to implement a fully functional exchange rate provider based on the real-world public data source of the assigned bank. By default, the information is obtained from the Czech National Bank, but the system is designed to be easily extensible to other providers in the future.

Design justification

I'm aware that it can be solved more simply, without so much over-engineering, but I'm doing it this way precisely to be consistent with the topics discussed in the interview. I intend to illustrate some topics such as clean architecture, best practices in API design, SOLID principles or performance improvement.

For this reason, and because the exercise should be treated as production code, I have decided to spend some more time building an extensible and maintainable architecture and implementing more advanced aspects such as or securitization, caching, resilience or CQRS.

Requirements

  • .NET 8

How to run the application

  • Clone the repository
  • Open the solution in Visual Studio to run and debug it.
  • A web browser should open automatically, otherwise open a browser and navigate to https://localhost:7166/index.html to interact with the exchange rater API using Swagger UI.
  • You can also run tests from Visual Studio.
  • IMPORTANT: This is a secure API. You can use the key "M1S3cur34P1K31F0rT3st1ng" to make requests.

API architecture

We design our service following the principles of Clean Architecture DDD. Then, we structure the code in 4 layers:

imagen

ExchangeRateUpdater.Domain layer and ExchangeRateUpdater.Application layer will be the core layers. And we have ExchangeRateUpdater.API layer, which is the presentation layer, and ExchangeRateUpdater.Infrastructure layer (we also call Periphery layers). The main idea behind Clean Architecture approach, is to separate the domain code from the application and infrastructure code, so that the core (business logic) of our software can evolve independently of the rest of the components. Regarding the DDD (Domain Drive Design) approach, it proposes a modeling based on business reality according to its use cases. The important thing is to organize the code so that it is aligned with the business problems and uses the same business terms (ubiquitous language).

Explaining the layers in more detail:

  • ExchangeRateUpdater.Domain: It must contain the domain entities and encapsulate their business logic. And should not have dependencies on other application layers. In this exercise, for simplicity, the ExchangeRate and Currency domain objects are treated as ValueObject, since they are considered to have no identity, are immutable, and two objects are equal if their values ​​are equal.
  • ExchangeRateUpdater.Application: This layer covers all business use cases, therefore it is responsible for aspects such as business use cases, business validations, business flows, etc. Work only with abstractions, delegating implementations to the infrastructure layer. Depends on Domain layer in order to use business entities and logic. We structure this layer in 3 main folders:
    • Contracts: represent business requirements. Includes the interfaces and contracts for the application. This folder should cover application capabilities. This should include interfaces for abstracting use cases implementations. We separate contracts into subfolders based on functionality.
    • Queries: Represents read-only business use cases. This folder will apply CQRS design pattern for handling business use cases (validation-handler-response). It will contain a subfolder for each use case. Is the heart of this layer.
    • Behaviours: It represent concepts that are transversal to all use cases. Includes the business validations or logging. Open to any other crosscutting concerns that apply when performing the use case implementations.
  • ExchangeRateUpdater.Infrastructure: operations related to external systems. This layer will include the implementations of the abstractions defined in the Application layer. Depends on Application layer in order to use core layers.
  • ExchangeRateUpdater.API: this layer expose API to external microservices. Depends on Application (to perform operations in controlers) and Infrastructure

Design patterns and best practices

  • Authentication
  • CQRS
  • Strategy pattern
  • Extensions
  • Dependency Inversion
  • Dependency Injection
  • Logging
  • Input Validation
  • Exception handling
  • Testing
  • Implement with SOLID principles in mind
  • Caching
  • Resilience in HttpClient
  • Returning the appropriate HTTP status codes

Third-party Nuget packages

  • Swashbuckle.AspNetCore: Swagger tools for documenting APIs built on ASP.NET Core
  • MediatR: Mediator pattern for ASP.NET Core. We use it in order to implement CQRS with Mediator pattern
  • FluentValidation: A validation library for .NET that uses a fluent interface to construct strongly-typed validation rules. We ise it to perform validations when applying CQRS, before execute commands.
  • Microsoft.Extensions.Http.Resilience: Resilience mechanisms for HttpClient
  • Microsoft.Extensions.Caching.Memory: Used for in-memory cache implementation
  • NetArchTest: to be able to testing architectural rules. We use it to testing the Clean Architecture rules
  • FluentASsertions: to test assertions
  • Moq: Moq is the most popular and friendly mocking framework for .NET.
  • xunit: as a developer testing framework.

Assumptions

  • We use an in-memory cache for simplicity
  • Unit tests do not cover 100% of the code. Only a few relevant examples will be implemented in each layer.

Possible improvements

  • Implement integration test
  • Dockerize the application to make it portable and self-contained, and facilitate its integration into the DevOps flow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant