Conception d'architecture de projet backend
🎯 Question centrale
Du simple script au système distribué à grande échelle, comment choisir la bonne architecture pour des projets backend de différentes tailles et langages ? C'est comme demander : d'un atelier artisanal à une grande usine, comment concevoir différentes lignes de production en fonction du volume et des processus ? Une bonne architecture backend doit évoluer avec la croissance de l'activité tout en tirant pleinement parti des caractéristiques du langage.
1. Évolution de l'architecture : du script au système
1.1 Niveaux d'architecture selon le nombre d'utilisateurs
L'architecture d'un projet backend doit correspondre à l'échelle de l'activité et au nombre d'utilisateurs :
| Niveau | Utilisateurs | Concurrence | Scénario type | Priorités |
|---|---|---|---|---|
| Débutant | < 1k | < 100 | Projet personnel, MVP, outil interne | Développement rapide, déploiement simple |
| Intermédiaire | 1k-100k | 100-10k | Système d'entreprise, SaaS, plateforme moyenne | Architecture en couches, normes de code |
| Entreprise | > 100k | > 10k | Grande plateforme, application internet | Microservices, haute disponibilité, optimisation des performances |
1.2 Choisir le style architectural selon les caractéristiques du langage
Chaque langage a sa propre philosophie de conception et son écosystème. La conception de l'architecture doit s'adapter aux caractéristiques du langage :
| Langage | Philosophie | Style architectural recommandé | Frameworks représentatifs |
|---|---|---|---|
| Node.js | Piloté par événements, I/O non bloquant | Architecture en couches + flux asynchrone | Express, NestJS, Fastify |
| Python | Simple et élégant, développement rapide | MTV/MVC, architecture en couches | Django, Flask, FastAPI |
| Go | Simple et efficace, concurrence native | Couches simples, microservices | Gin, Echo, Fiber |
| Java | Niveau entreprise, typage fort | Couches strictes, Domain-Driven Design | Spring Boot, Spring Cloud |
💡 Principes de choix architectural
- Ne pas sur-concevoir : les petits projets utilisent une architecture simple, seuls les grands projets nécessitent une architecture complexe
- S'adapter aux caractéristiques du langage : n'essayez pas d'écrire du code de style Java en Python
- Évolution progressive : commencez simplement, optimisez progressivement avec la croissance de l'activité
- Familiarité de l'équipe : choisissez un style architectural que l'équipe connaît pour réduire le coût d'apprentissage
2. Architecture débutant (utilisateurs < 1k)
2.1 Scénarios d'application
- Projets personnels, exercices d'apprentissage
- MVP de startup (Minimum Viable Product)
- Outils internes, back-office
- Validation de prototype, démonstration de concept
2.2 Node.js - Style script concis
Caractéristiques : fichier unique ou découpage simple, mise en ligne rapide
my-node-api/
├── src/
│ ├── app.js # Point d'entrée de l'application
│ ├── routes.js # Définition des routes
│ ├── db.js # Connexion à la base de données
│ └── utils.js # Fonctions utilitaires
├── .env # Variables d'environnement
├── package.json
└── README.mdExemple de code :
// src/app.js
const express = require('express');
const app = express();
app.use(express.json());
// Les routes sont écrites directement dans le point d'entrée (adapté quand il y a très peu d'endpoints)
app.get('/users', async (req, res) => {
const users = await db.query('SELECT * FROM users');
res.json(users);
});
app.post('/users', async (req, res) => {
const { name, email } = req.body;
const result = await db.query(
'INSERT INTO users (name, email) VALUES (?, ?)',
[name, email]
);
res.status(201).json({ id: result.insertId });
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});Projets open source de référence :
- expressjs/express - Exemples officiels
- vercel/micro - Style microservices
2.3 Python - Style prototypage rapide
Caractéristiques : tirer parti de la simplicité de Python pour implémenter rapidement des fonctionnalités
my-python-api/
├── app.py # Application principale
├── models.py # Modèles de données
├── config.py # Configuration
├── requirements.txt
└── README.mdExemple de code (Flask) :
# app.py
from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
db = SQLAlchemy(app)
# Définition du modèle
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
# Routes
@app.route('/users', methods=['GET'])
def get_users():
users = User.query.all()
return jsonify([{'id': u.id, 'name': u.name, 'email': u.email} for u in users])
@app.route('/users', methods=['POST'])
def create_user():
data = request.json
user = User(name=data['name'], email=data['email'])
db.session.add(user)
db.session.commit()
return jsonify({'id': user.id}), 201
if __name__ == '__main__':
app.run(debug=True)Projets open source de référence :
- pallets/flask - Exemples officiels
- tiangolo/fastapi - Style asynchrone moderne
2.4 Go - Style bibliothèque standard concise
Caractéristiques : utiliser la bibliothèque standard de Go, un minimum de dépendances
my-go-api/
├── main.go # Point d'entrée
├── handlers.go # Gestionnaires
├── models.go # Modèles
├── db.go # Base de données
├── go.mod
└── README.mdExemple de code :
// main.go
package main
import (
"database/sql"
"encoding/json"
"log"
"net/http"
_ "github.com/mattn/go-sqlite3"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
var db *sql.DB
func main() {
var err error
db, err = sql.Open("sqlite3", "./app.db")
if err != nil {
log.Fatal(err)
}
http.HandleFunc("/users", usersHandler)
log.Println("Server starting on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
func usersHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
getUsers(w, r)
case http.MethodPost:
createUser(w, r)
}
}
func getUsers(w http.ResponseWriter, r *http.Request) {
rows, _ := db.Query("SELECT id, name, email FROM users")
defer rows.Close()
var users []User
for rows.Next() {
var u User
rows.Scan(&u.ID, &u.Name, &u.Email)
users = append(users, u)
}
json.NewEncoder(w).Encode(users)
}Projets open source de référence :
- golang/go - Exemples de la bibliothèque standard
- go-chi/chi - Routeur léger
2.5 Java - Style démarrage Spring Boot
Caractéristiques : utiliser la configuration automatique de Spring Boot pour un démarrage rapide
my-spring-app/
├── src/main/java/com/example/
│ ├── controller/
│ │ └── UserController.java
│ ├── model/
│ │ └── User.java
│ ├── repository/
│ │ └── UserRepository.java
│ └── Application.java
├── src/main/resources/
│ └── application.yml
├── pom.xml
└── README.mdExemple de code :
// Application.java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// User.java
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// getters and setters
}
// UserRepository.java
public interface UserRepository extends JpaRepository<User, Long> {
}
// UserController.java
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping
public List<User> getAllUsers() {
return userRepository.findAll();
}
@PostMapping
public User createUser(@RequestBody User user) {
return userRepository.save(user);
}
}Projets open source de référence :
- spring-projects/spring-boot - Exemples officiels
- spring-projects/spring-petclinic - Exemple classique
3. Architecture intermédiaire (utilisateurs 1k-100k)
3.1 Scénarios d'application
- Systèmes de gestion d'entreprise (ERP, CRM, OA)
- Applications SaaS
- Plateformes e-commerce
- Projets nécessitant une collaboration multi-équipes
3.2 Détail de l'architecture en couches
Pour les projets de niveau intermédiaire, il est recommandé d'adopter une architecture en quatre couches (Controller-Service-Repository-Model) :
project/
├── src/
│ ├── controllers/ # Couche contrôleur : traite les requêtes HTTP
│ ├── services/ # Couche service : logique métier
│ ├── repositories/ # Couche données : accès aux données
│ ├── models/ # Couche modèle : structures de données
│ ├── middlewares/ # Middlewares
│ ├── utils/ # Fonctions utilitaires
│ ├── config/ # Configuration
│ └── routes/ # Définition des routes
├── tests/
├── docs/
└── scripts/3.3 Node.js - Architecture en couches entreprise
Projets open source de référence :
- nestjs/nest - Framework Node.js entreprise
- goldbergyoni/nodebestpractices - Bonnes pratiques Node.js
node-enterprise/
├── src/
│ ├── modules/ # Organisé par modules fonctionnels
│ │ ├── users/
│ │ │ ├── users.controller.ts
│ │ │ ├── users.service.ts
│ │ │ ├── users.repository.ts
│ │ │ ├── users.module.ts
│ │ │ └── dto/
│ │ ├── orders/
│ │ └── products/
│ ├── common/ # Modules partagés
│ │ ├── filters/ # Filtres d'exception
│ │ ├── guards/ # Gardes
│ │ ├── interceptors/ # Intercepteurs
│ │ └── pipes/ # Pipes
│ ├── config/
│ └── main.tsExemple de code NestJS :
// users/users.controller.ts
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get()
findAll(@Query() query: QueryUserDto) {
return this.usersService.findAll(query);
}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
}
// users/users.service.ts
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private usersRepository: Repository<User>,
) {}
async findAll(query: QueryUserDto) {
const [data, total] = await this.usersRepository.findAndCount({
skip: (query.page - 1) * query.limit,
take: query.limit,
});
return { data, total };
}
async create(createUserDto: CreateUserDto) {
const user = this.usersRepository.create(createUserDto);
return this.usersRepository.save(user);
}
}3.4 Python - Style Django/DRF
Projets open source de référence :
- django/django - Projet officiel
- encode/django-rest-framework - Framework REST
- cookiecutter/cookiecutter-django - Template de projet
django-enterprise/
├── apps/
│ ├── users/ # Application utilisateurs
│ │ ├── models.py
│ │ ├── views.py # Vues API
│ │ ├── serializers.py # Sérialiseurs
│ │ ├── permissions.py # Permissions
│ │ ├── urls.py
│ │ └── tests/
│ ├── orders/
│ └── products/
├── config/ # Configuration du projet
│ ├── settings/
│ │ ├── base.py
│ │ ├── development.py
│ │ └── production.py
│ ├── urls.py
│ └── wsgi.py
├── utils/ # Utilitaires partagés
├── templates/
├── static/
└── manage.pyExemple de code Django REST Framework :
# users/models.py
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
phone = models.CharField(max_length=20, blank=True)
avatar = models.URLField(blank=True)
# users/serializers.py
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'email', 'phone', 'avatar']
# users/views.py
from rest_framework import viewsets, permissions
from rest_framework.decorators import action
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]
@action(detail=False, methods=['get'])
def me(self, request):
serializer = self.get_serializer(request.user)
return Response(serializer.data)
# users/urls.py
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = router.urls3.5 Go - Style Clean Architecture
Projets open source de référence :
- gin-gonic/gin - Framework Web
- go-kit/kit - Boîte à outils microservices
- bxcodec/go-clean-arch - Exemple Clean Architecture
go-enterprise/
├── cmd/
│ └── api/ # Point d'entrée de l'application
│ └── main.go
├── internal/ # Code privé
│ ├── domain/ # Couche domaine (entités, interfaces)
│ │ ├── user.go
│ │ └── repository.go
│ ├── usecase/ # Couche cas d'usage (logique métier)
│ │ └── user_usecase.go
│ ├── delivery/ # Couche transport (HTTP/gRPC)
│ │ └── http/
│ │ └── user_handler.go
│ ├── repository/ # Couche repository (accès aux données)
│ │ └── user_repository.go
│ └── config/
├── pkg/ # Bibliothèque publique
├── migrations/
└── go.modExemple de code Clean Architecture :
// domain/user.go
type User struct {
ID int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
// domain/repository.go
type UserRepository interface {
GetByID(ctx context.Context, id int64) (*User, error)
GetByEmail(ctx context.Context, email string) (*User, error)
Create(ctx context.Context, user *User) error
Update(ctx context.Context, user *User) error
}
// usecase/user_usecase.go
type UserUsecase struct {
userRepo UserRepository
}
func (u *UserUsecase) GetByID(ctx context.Context, id int64) (*User, error) {
return u.userRepo.GetByID(ctx, id)
}
func (u *UserUsecase) Create(ctx context.Context, user *User) error {
// Logique métier : vérifier si l'email existe déjà
existing, _ := u.userRepo.GetByEmail(ctx, user.Email)
if existing != nil {
return errors.New("email already exists")
}
return u.userRepo.Create(ctx, user)
}
// delivery/http/user_handler.go
type UserHandler struct {
UserUsecase *usecase.UserUsecase
}
func (h *UserHandler) GetUser(c *gin.Context) {
id, _ := strconv.ParseInt(c.Param("id"), 10, 64)
user, err := h.UserUsecase.GetByID(c.Request.Context(), id)
if err != nil {
c.JSON(404, gin.H{"error": "user not found"})
return
}
c.JSON(200, user)
}3.6 Java - Spring Boot niveau entreprise
Projets open source de référence :
- spring-projects/spring-boot
- spring-cloud-samples - Exemples microservices
- ali-baba/spring-cloud-alibaba - Microservices Alibaba
spring-enterprise/
├── src/main/java/com/example/
│ ├── application/ # Couche application
│ │ ├── controller/ # Contrôleurs
│ │ ├── dto/ # Objets de transfert de données
│ │ └── assembler/ # Assembleurs
│ ├── domain/ # Couche domaine
│ │ ├── entity/ # Entités
│ │ ├── valueobject/ # Objets valeur
│ │ ├── repository/ # Interfaces repository
│ │ └── service/ # Services domaine
│ ├── infrastructure/ # Couche infrastructure
│ │ ├── repository/ # Implémentations repository
│ │ ├── config/ # Configuration
│ │ └── common/ # Classes utilitaires
│ └── Application.java
├── src/main/resources/
│ ├── application.yml
│ └── mapper/
└── src/test/Exemple de code Domain-Driven Design (DDD) :
// domain/entity/User.java
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String username;
@Column(nullable = false, unique = true)
private String email;
@Embedded
private UserStatus status;
// Méthodes domaine
public void deactivate() {
this.status = UserStatus.INACTIVE;
}
public boolean isActive() {
return this.status == UserStatus.ACTIVE;
}
}
// domain/repository/UserRepository.java
public interface UserRepository {
Optional<User> findById(Long id);
Optional<User> findByEmail(String email);
User save(User user);
void delete(User user);
}
// application/controller/UserController.java
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
private final UserAssembler userAssembler;
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
User user = userService.findById(id);
return ResponseEntity.ok(userAssembler.toDTO(user));
}
@PostMapping
public ResponseEntity<UserDTO> createUser(@RequestBody @Valid CreateUserRequest request) {
User user = userService.createUser(request);
return ResponseEntity.status(HttpStatus.CREATED)
.body(userAssembler.toDTO(user));
}
}
// infrastructure/repository/UserRepositoryImpl.java
@Repository
@RequiredArgsConstructor
public class UserRepositoryImpl implements UserRepository {
private final UserJpaRepository jpaRepository;
@Override
public Optional<User> findById(Long id) {
return jpaRepository.findById(id);
}
@Override
public User save(User user) {
return jpaRepository.save(user);
}
}4. Architecture entreprise (utilisateurs > 100k)
4.1 Scénarios d'application
- Grandes plateformes internet
- Systèmes de trading financier
- Systèmes e-commerce à haute concurrence
- Grands projets nécessitant une collaboration multi-équipes
4.2 Architecture microservices
Lorsqu'une application monolithique ne répond plus aux besoins, il faut envisager une architecture microservices :
microservices-platform/
├── api-gateway/ # Passerelle API
│ ├── src/
│ └── Dockerfile
├── services/ # Services métier
│ ├── user-service/ # Service utilisateurs
│ ├── order-service/ # Service commandes
│ ├── product-service/ # Service produits
│ └── payment-service/ # Service paiements
├── shared/ # Bibliothèques partagées
│ ├── proto/ # Protocol Buffers
│ ├── common-lib/
│ └── event-contracts/
├── infrastructure/ # Infrastructure
│ ├── docker-compose.yml
│ ├── kubernetes/
│ └── terraform/
└── docs/4.3 Frameworks microservices par langage
| Langage | Framework microservices | Découverte de services | Centre de configuration | Traçage distribué |
|---|---|---|---|---|
| Node.js | NestJS + gRPC | Consul | etcd | Jaeger |
| Python | FastAPI + Nameko | Eureka | Consul | Zipkin |
| Go | Go-kit + gRPC | etcd | etcd | OpenTelemetry |
| Java | Spring Cloud | Nacos | Nacos | SkyWalking |
4.4 Conception du dépôt de code (Monorepo vs Polyrepo)
Monorepo (dépôt unique) :
monorepo/
├── services/
│ ├── user-service/ # Service indépendant
│ │ ├── src/
│ │ ├── package.json
│ │ └── Dockerfile
│ ├── order-service/
│ └── product-service/
├── shared/
│ ├── types/ # Types partagés
│ ├── utils/ # Utilitaires partagés
│ └── proto/ # Protocoles partagés
├── packages/
│ ├── eslint-config/ # Configuration ESLint partagée
│ └── ts-config/ # Configuration TS partagée
├── docker-compose.yml
└── package.json # package.json racineAvantages :
- Partage de code facilité
- Build et déploiement unifiés
- Refactoring aisé
Inconvénients :
- Dépôt de code volumineux
- Gestion des permissions complexe
Polyrepo (dépôts multiples) :
Chaque service dans un dépôt indépendant :
github.com/company/user-servicegithub.com/company/order-servicegithub.com/company/shared-lib
Avantages :
- Évolution indépendante des services
- Autonomie des équipes
- Permissions claires
Inconvénients :
- Partage de code difficile
- Gestion des versions complexe
4.5 Conception de la couche données
Stratégie de choix de base de données :
| Type de données | Base de données recommandée | Scénarios d'application |
|---|---|---|
| Données relationnelles | PostgreSQL | Utilisateurs, commandes, produits |
| Cache | Redis | Sessions, données chaudes |
| Recherche | Elasticsearch | Recherche de produits, logs |
| Données temporelles | InfluxDB/TimescaleDB | Monitoring, métriques |
| Données document | MongoDB | Logs, configuration |
Conception de la couche d'accès aux données :
data-layer/
├── primary-db/ # Base de données principale
│ ├── master/ # Base d'écriture
│ └── slaves/ # Bases de lecture
├── cache-layer/ # Couche de cache
│ ├── redis-cluster/
│ └── local-cache/
├── search-engine/ # Moteur de recherche
│ └── elasticsearch/
└── message-queue/ # File de messages
├── kafka/
└── rabbitmq/5. Références de normes architecturales des projets open source
5.1 Écosystème Node.js
Structure de projet officielle Express.js :
express-project/
├── bin/ # Scripts de démarrage
├── public/ # Ressources statiques
├── routes/ # Routes
├── views/ # Vues
├── app.js # Configuration de l'application
└── package.jsonRecommandation officielle NestJS :
nest-project/
├── src/
│ ├── modules/ # Modules fonctionnels
│ ├── common/ # Modules partagés
│ ├── config/
│ └── main.ts
├── test/
└── nest-cli.json5.2 Écosystème Python
Structure de projet officielle Django :
django-project/
├── project_name/ # Configuration du projet
├── apps/ # Répertoire des applications
├── templates/
├── static/
├── media/
└── manage.pyStructure de projet FastAPI :
fastapi-project/
├── app/
│ ├── api/
│ │ ├── deps.py # Dépendances
│ │ └── v1/
│ │ └── endpoints/
│ ├── core/ # Configuration centrale
│ ├── db/ # Base de données
│ ├── models/ # Modèles
│ ├── schemas/ # Modèles Pydantic
│ └── main.py
├── tests/
└── alembic/ # Migrations5.3 Écosystème Go
Disposition de projet standard :
go-project/
├── cmd/ # Point d'entrée de l'application
│ └── app/
│ └── main.go
├── internal/ # Code privé
├── pkg/ # Bibliothèque publique
├── api/ # Définition API
├── web/ # Ressources statiques
├── configs/ # Configuration
├── scripts/ # Scripts
└── go.modRéférence :
5.4 Écosystème Java
Structure officielle Spring Boot :
spring-boot-project/
├── src/main/java/com/example/
│ ├── controller/
│ ├── service/
│ ├── repository/
│ ├── entity/
│ ├── dto/
│ ├── config/
│ └── Application.java
├── src/main/resources/
│ ├── static/
│ ├── templates/
│ └── application.yml
└── src/test/Manuel de développement Java Alibaba :
- Couches claires : controller/service/manager/dao
- Modèles domaine : distinction DO/DTO/BO/VO
- Structure des packages : organisation par module fonctionnel
6. Feuille de route d'évolution architecturale
6.1 Exemple d'évolution
Phase 1 : Application monolithique (débutant)
↓ Croissance du nombre d'utilisateurs, élargissement de l'équipe
Phase 2 : Architecture en couches (intermédiaire)
↓ Complexité métier, collaboration multi-équipes
Phase 3 : Modularisation/microservices (entreprise)
↓ Exigences de haute concurrence, haute disponibilité
Phase 4 : Architecture cloud native (plateforme)6.2 Quand faire évoluer l'architecture ?
| Signal | Niveau actuel | Mise à niveau suggérée |
|---|---|---|
| Fichiers de code > 50 | Débutant | Intermédiaire |
| Temps de build > 5 minutes | Intermédiaire | Modularisation |
| Équipe > 10 personnes | Intermédiaire | Microservices |
| Utilisateurs actifs quotidiens > 100k | Intermédiaire | Entreprise |
| Stack technique multi-langages | Monolithique | Microservices |
7. Résumé
💡 Idée centrale
L'architecture est au service du métier, pas l'inverse.
Choix selon le nombre d'utilisateurs :
- < 1k : Script simple, mise en ligne rapide
- 1k-100k : Architecture en couches, normes de code
- > 100k : Microservices, conception haute disponibilité
Choix selon le langage :
- Node.js : Exploiter les caractéristiques asynchrones, adapté aux applications I/O intensives
- Python : Développement rapide, adapté au traitement de données et à l'IA
- Go : Haute performance, adapté au cloud native et aux microservices
- Java : Niveau entreprise, adapté aux grands systèmes complexes
Principes généraux :
- Évolution progressive : Commencer simplement, évoluer avec la croissance de l'activité
- Convention plutôt que configuration : Uniformiser les normes, réduire les coûts de communication
- Tests automatisés : Garantir la sécurité du refactoring
- Documentation d'abord : Consigner les décisions architecturales
Objectif final : Faire en sorte que le code fonctionne aussi efficacement qu'une chaîne de production, quelle que soit son échelle.
Ressources de référence
Projets open source
- nestjs/nest - Framework Node.js entreprise
- django/django - Framework Web Python
- gin-gonic/gin - Framework Web Go
- spring-projects/spring-boot - Framework Java
Guides d'architecture
- goldbergyoni/nodebestpractices - Bonnes pratiques Node.js
- golang-standards/project-layout - Disposition de projet Go
- cookiecutter/cookiecutter-django - Template de projet Django
- ali-baba/spring-cloud-alibaba - Microservices Alibaba
Livres
- 《Clean Architecture》- Robert C. Martin
- 《Building Microservices》- Sam Newman
- 《Designing Data-Intensive Applications》- Martin Kleppmann