Backend Design— Actix-web Project Hierarchy

Micromike
Geek Culture
Published in
4 min readSep 30, 2021

I use Actix-web as my backend web framework. By borrowing the idea of Django, I think it would be a good idea to share some of my thoughts about the source code hierarchy when implementing a backend application with a slightly medium size scale.

Introduction

Currently I have the oppotunity to help implement a backend service called healyou.

Healyou is a service that help people easly arrange a remote meeting with mental consultant.

My responsibility is to implement a bunch of REST APIs so that the service can record consultant, patient and reservation status.

What we will cover in this post

In this post, I will try to walk through the following topics

  • The Importance of Source Code Hierarchy
  • Model/Controller

The Importance of Source Code Hierarchy

Source code hierarchy plays a very important role when developing a project.

When the project grows bigger and other people try to understand your code, the very first things would be (1) reading the document, (2) tracing your source code.

When tracing code, a good source code hierarchy can help other developer catch up your code more quickly.

In Django, there is a manage.py which will help you create project and app inside your project.

Just like fuseki (布局) in go, based on the experiences of other developer, the source code files are well organized and for other people familiar with Django, it surely improve the time to understand the entire project.

Actix-web on the other hand does not have such tools to help your organize your project and apps.

Moreover there is no official guideline talking about how to place your code properly.

As a result, I decide to borrow the idea of Django, and design the source code hierarchy for a better maintainability.

The Goal of The Design

What I want to achieve is when adding or modifying a endpoint (/api/v1/consultant), the only code that will be changed will be the code that is related to the endpoint.

Like Python, in Rust, we can organize codes into a module.

The following is a snippet of the source code hierarchy.

src/
├── app
│ ├── mod.rs
│ ├── consultant
│ │ ├── errors.rs
│ │ ├── mod.rs
│ │ ├── models.rs
│ │ ├── tests.rs
│ │ ├── urls.rs
│ │ └── views.rs
│ ├── patient
│ │ ├── errors.rs
│ │ ├── mod.rs
│ │ ├── models.rs
│ │ ├── tests.rs
│ │ ├── urls.rs
│ │ └── views.rs
│ └── utils.rs
├── db.rs
└── main.rs

Inside the source folder, there are

  • main module
  • db module
  • app::consultant module
  • app::patient module
  • app::utils module

1. main Module

The main module is responsible for setting up the actix_web:app and initialize the main loop for the whole service.

The main.rs is very unlikely to change when modifying the API implementation content.

2. db Module

The db module is responsible for setting up the database connection pool for the API model which require to access the db.

3. app Module

One of the things that I need to mention for the app::mod.rs file is the register_urls function.

I separate the url registration logic into app modules so that main.rs do not need to change whenever a route is being modified.

4. app::utils Module

The utils module is a set of utility functions and structure that will be used by other API implementation.

Model/Controller

Since the service is a REST API server, which means there is no view for this project.

As a result, when designing the source code hierarchy, I only take the follwoing metrics into consideration.

  • Route
  • Controller
  • Model
  • Error Code
  • Tests

Each app::$function module represent a separate functionality of the services.

e.g. app::consultant is responsible for dealing with consultant CRUD.

│   ├── consultant
│ │ ├── errors.rs
│ │ ├── mod.rs
│ │ ├── models.rs
│ │ ├── tests.rs
│ │ ├── urls.rs
│ │ └── views.rs

1. Route

urls.rs is responsible for defining the route of the API endpoint.

2. Controller

views.rs is the core controller business logic for the service endpoint.

3. Models

models.rs is responsible for implementing the db related operation and structure.

In Django, it use ORM to represent DB data.

But in my current implementation, I use sqlx and some custom struct with Rust From trait to try to achieve the same effect.

4. Errors

Error code is very important when trying to narrow down the root cause of a issue.

The errors.rs is responsible for defining the error code for the current endpoint and used by the controller itself.

5. Tests

Finally the testing part.

The tests.rs is responsible for verify the controller and models are working perfectly.

Conclusion

This post breifly share some of my thoughts and experiences when developing REST API service with actix-web.

By using this philosophy, the whole project can be much readable and maintainable.

--

--

Micromike
Geek Culture

Senior product developer in Synology. Programmer, #infosec enthusiasm, #linux, #python #Rust