Structure

The folder structure of a project is very important. It helps you to organize your project in a better way. It also helps you to understand the project structure easily.

In this guide, we will discuss the folder structure of a project.


Minimal structure

The minimal structure proposed by the project consists of a simple source folder containing your business code. The bin folder is used to use the hmr component in your application during the development phase; you can change the name of the source folder, but we do not recommend deleting it.

├── bin
│  └── main.dart

├── lib
│  ├── events
│  └── commands

├── pubspec.yaml
└── .env

This architecture is classic in the world of Discord bots. It’s simple yet meets the need for rapid implementation.

One of the limitations arises when your application grows more or less drastically: you’ll end up with a multitude of files at the same level, which can lead to poor understanding of your application.


N-tier structure

When you’re developing increasingly complex applications, you generally lose readability and maintainability and maintainability if you don’t focus on a stable, future-proof software architecture.

Note

We recommend the following structure.

One approach we recommend is the module-based architecture. This involves dividing your application into several business modules that are independent of each other.

├── assets
│  ├── image1.png
│  └── image2.png

├── bin
│  └── main.dart

├── lib
│  ├── commons
│  │  ├── helper.dart
│  │  └── api.dart
│  │
│  ├── first_module
│  │  ├── events
│  │  ├── commands
│  │  └── provider.dart
│  │
│  └── second_module
│     ├── events
│     ├── commands
│     └── provider.dart

├── pubspec.yaml
└── .env

This architecture has several notable advantages: it allows you to:

  • divide your application code into modules, each representing a business fragment
  • simplifies the integration of your code across multiple projects thanks to providers

In this architecture, we use providers allowing us to “move” the entry point of our module in order to facilitate the segmentation of our code.

To simplify the management of your modules, we recommend that you create a provider.dart file within each which will serve as the entry point for each module.

final client = Client()
  .registerProvider(Provider.new) 
  .build();

Layered architecture

When we talk about modular architecture, we quickly come to hexagonal architecture. This architecture allows you to decouple the different parts of your application according to their core business and responsibility within it.

Note

We recommend the following structure.

├── bin
│  └── main.dart

├── lib
│  ├── ui
│  │  ├── events
│  │  │  ├── ready_event.dart
│  │  │  └── message_create_event.dart
│  │  │
│  │  └── commands
│  │     └── foo_command.dart
│  │
│  ├── services
│  │  └── foo_service.dart
│  │
│  ├── data
│  │  ├── models
│  │  └── repositories
│  │
│  └── provider.dart

├── pubspec.yaml
└── .env

The above structure is an example that we recommend to help you adopt a modular, scalable and maintainable architecture by decoupling the different parts of your application as much as possible. However, it will not always suit your business needs and will need to be adapted accordingly.

DirectoryDescription
uiContains the various parts of your application which are related to your business, including events and commands.
servicesContains the services which allow you to manipulate the data in your application; they will generally be used from the ui.
dataContains the DTOs and repositories that allow you to manipulate the data in your application. An example of use is the use of a remote REST service to which you need to make HTTP calls.
providerThe entry point of your module who’s call under the main file.

Hexagonal architecture

When we talk about modular architecture, we quickly come to hexagonal architecture. This architecture allows you to decouple the different parts of your application according to their core business and responsibility within it.

Note

We recommend the following structure.

├── bin
│  └── main.dart

├── lib
│  └── application
│  │  └── tickets
│  │     ├── commands
│  │     ├── events
│  │     └── ticket_provider.dart
│  │
│  ├── domain
│  │  └── tickets
│  │     ├── entities
│  │     ├── repositories
│  │     ├── dtos
│  │     └── contracts
│  │
│  └── infrastructure
│     └── tickets
│        ├── repositories
│        └── dtos

├── pubspec.yaml
└── .env

This architecture has several notable advantages: it allows you to:

  • divide your application code into modules, each representing a business fragment
  • simplifies the integration of your code across multiple projects thanks to providers
  • isolate the business logic of your application from the infrastructure layer

The application directory contains all of our business context divided into modules, allowing better readability and maintainability of our code.

DirectoryDescription
binContains the entry point of your application.
applicationContains the various parts of your application which are related to your business, including events, commands, states or any mineral component, it can be split into modules.
domainContains the entities, DTOs, repositories, contracts that allow you to manipulate the data in your application. He contains the business logic of your application and must not depend on anything other than itself.
infrastructureContains implementations that must interact with external services such as APIs, BDDs and queues. Each implementation must implement a contract defined in the domain.
Note

You can find a sample implementation of this architecture here.