DEV Community

Sergio Gurillo Corral
Sergio Gurillo Corral

Posted on

An Opinionated Clean Architecture in Rust

As I mentioned in my previous post, I’ve been tinkering a lot with Rust over the past few months and really enjoying the development experience it offers. Honestly, it’s been ages since learning a new language felt this fun and challenging.

During my years of experience, I’ve mostly developed web services (with Java, PHP, Node, Python...) and now also with Rust. Since architectures like hexagonal, Clean Architecture, and DDD are in high demand, I wanted to try applying all these ideas to Rust to see how they fit.

After a lot of discussion with a colleague (thank you Alberto for your patience and time spent on this) about the right design for a project, we arrived at something like this:


Business

├── business                      # Business logic layer
│   ├── Cargo.toml                 # Rust library config for business logic
│   └── src
│       ├── application            # Use cases and application logic
│       │   └── product
│       │       └── use_cases      # CRUD operations for Product
│       │           ├── create.rs
│       │           ├── delete.rs 
│       │           ├── get_by.rs 
│       │           └── update.rs
│       ├── domain                 # Pure domain model, business rules
│       │   └── product
│       │       ├── errors.rs      # Domain-specific error definitions
│       │       ├── model.rs       # Product entity (attributes and behavior)
│       │       ├── repository.rs  # Interface for product data access
│       │       ├── use_cases      # Domain logic for specific cases
│       │       │   ├── create.rs
│       │       │   ├── delete.rs
│       │       │   ├── get_by.rs
│       │       │   └── update.rs
│       │       └── value_objects.rs # Value objects related to Product
│       ├── lib.rs                 # Entry point for business library
Enter fullscreen mode Exit fullscreen mode

This layer is the heart of the project, completely agnostic of any third-party dependencies. It holds all the business logic, the pure domain, and use cases representing core rules and behaviors — without letting technical or infrastructure details muddy the logic. In Rust, this separation is very clear thanks to modularity and organizing by crates.

Infrastructure

├── infrastructure                # Technical implementations (DB, events, notifications)
│   ├── README.md
│   ├── events                   # Integration with messaging/event systems
│   │   ├── Cargo.toml           # Rust library config for events
│   │   ├── kafka.rs            
│   │   └── rabbitmq.rs          
│   ├── notifications            # External notification services
│   │   ├── Cargo.toml           # Rust library config for notifications
│   │   ├── sendgrid.rs          
│   │   └── twilio.rs           
│   └── persistence              # Data persistence (databases)
│       ├── Cargo.toml           # Rust library config for DB
│       └── mongo
│           └── product
│               ├── data_model.rs   # DB data model (mapping)
│               └── repository.rs   # Mongodb repository implementation
Enter fullscreen mode Exit fullscreen mode

In this layer we put all the technical implementations that support the app, like databases, messaging, or external services. Because the business layer depends only on interfaces, here we can easily swap implementations without affecting the core logic. Rust’s type system and modules help keep these dependencies clear and controlled, and also make it easy to integrate with external tech via specific crates for Kafka, RabbitMQ, or notification services.

Presentation

├── presentation                 # User interface / system entry point
│   ├── README.md                
│   ├── cli                     # Command-line interface
│   │   ├── Cargo.toml
│   │   └── src
│   │       └── main.rs          # CLI main entry point
│   ├── cron                    # Scheduled jobs (recurring tasks)
│   │   ├── Cargo.toml
│   │   └── src
│   │       └── main.rs          # Code to run cron tasks
│   └── rest-api                # REST API exposing the domain
│       ├── Cargo.toml
│       └── src
│           ├── config          # API general configuration
│           │   ├── app_state.rs
│           │   ├── settings.rs
│           │   └── tracing.rs
│           ├── lib.rs          # REST library entry point
│           ├── main.rs         # Main to launch HTTP server
│           └── router
│               └── product
│                   ├── controller.rs  # REST controllers for Product
│                   └── dto.rs         # Data transfer objects
Enter fullscreen mode Exit fullscreen mode

Finally, the presentation layer is the system’s entry door, handling interaction with users or other external systems via APIs, CLI, scheduled jobs, or a playground to quickly test use cases locally. Keeping this layer separate ensures the details of how we receive or send data don’t mess with business logic or infrastructure.


Conclusion

I’m really happy with how the final design turned out. It lets me focus on the functionality and business logic without worrying about technical infrastructure details. Plus, isolating specific technologies helps reduce cognitive load and makes the project more maintainable and scalable. In short, I believe this architecture is a great way to organize a full, professional Rust project.

shop-project/
├── Cargo.toml                     # Main Rust project config file
├── business                      # Business logic layer
│   ├── Cargo.toml                 # Rust library config for business logic
│   └── src
│       ├── application            # Use cases and application logic
│       │   └── product
│       │       └── use_cases      # Operations for Product
│       │           ├── create.rs
│       │           ├── delete.rs 
│       │           ├── get_by.rs 
│       │           └── update.rs
│       ├── domain                 # Pure domain model, business rules
│       │   └── product
│       │       ├── errors.rs      # Domain-specific error definitions
│       │       ├── model.rs       # Product entity (attributes and behavior)
│       │       ├── repository.rs  # Interface for product data access
│       │       ├── use_cases      # Domain logic for specific cases
│       │       │   ├── create.rs
│       │       │   ├── delete.rs
│       │       │   ├── get_by.rs
│       │       │   └── update.rs
│       │       └── value_objects.rs # Value objects related to Product
│       ├── lib.rs                 # Entry point for business library
├── docker-compose.yml             # Docker services configuration
├── infrastructure                # Technical implementations (DB, events, notifications)
│   ├── README.md
│   ├── events                   # Integration with messaging/event systems
│   │   ├── Cargo.toml           # Rust library config for events
│   │   ├── kafka.rs            
│   │   └── rabbitmq.rs          
│   ├── notifications            # External notification services
│   │   ├── Cargo.toml           # Rust library config for notifications
│   │   ├── sendgrid.rs          
│   │   └── twilio.rs           
│   └── persistence              # Data persistence (databases)
│       ├── Cargo.toml           # Rust library config for DB        
│       └── mongo
│           └── product
│               ├── data_model.rs   # DB data model (mapping)
│               └── repository.rs   # Mongo repository implementation
├── presentation                 # User interface / system entry point
│   ├── README.md                
│   ├── cli                     # Command-line interface
│   │   ├── Cargo.toml
│   │   └── src
│   │       └── main.rs          # CLI main entry point
│   ├── cron                    # Scheduled jobs (recurring tasks)
│   │   ├── Cargo.toml
│   │   └── src
│   │       └── main.rs          # Code to run cron tasks
│   └── rest-api                # REST API exposing the domain
│       ├── Cargo.toml
│       └── src
│           ├── config          # API general configuration
│           │   ├── app_state.rs
│           │   ├── settings.rs
│           │   └── tracing.rs
│           ├── lib.rs          # REST library entry point
│           ├── main.rs         # Main to launch HTTP server
│           └── router
│               └── product
│                   ├── controller.rs  # REST controllers for Product
│                   └── dto.rs         # Data transfer objects
Enter fullscreen mode Exit fullscreen mode

Top comments (0)