Skip to content

Arquitetura do Sistema

O CSGOFlip segue os princípios de Clean Architecture, garantindo separação de responsabilidades, testabilidade e manutenibilidade.

Por que Clean Architecture?

Benefícios

  • Independência de frameworks: A lógica de negócio não depende do NestJS
  • Testabilidade: Use cases podem ser testados sem banco de dados ou HTTP
  • Independência de UI: O mesmo backend serve web, mobile e admin
  • Independência de banco: Trocar de PostgreSQL para outro DB é possível
  • Independência de agentes externos: APIs externas são abstraídas

Diagrama de Camadas

┌─────────────────────────────────────────────────────────────────────┐
│                        PRESENTATION LAYER                            │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌────────────┐  │
│  │ Controllers │  │   Guards    │  │   Pipes     │  │  Modules   │  │
│  │  (HTTP)     │  │  (Auth)     │  │ (Validate)  │  │  (NestJS)  │  │
│  └──────┬──────┘  └─────────────┘  └─────────────┘  └────────────┘  │
│         │                                                            │
│         │  DTOs (Request/Response)                                   │
└─────────┼────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────┐
│                        APPLICATION LAYER                             │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │                        USE CASES                             │    │
│  │  ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │    │
│  │  │ OpenCase     │ │ CreateBattle │ │ ProcessWithdrawal    │ │    │
│  │  │ UseCase      │ │ UseCase      │ │ UseCase              │ │    │
│  │  └──────────────┘ └──────────────┘ └──────────────────────┘ │    │
│  └─────────────────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │                    APPLICATION SERVICES                      │    │
│  │  ┌────────────────┐ ┌────────────────┐ ┌─────────────────┐  │    │
│  │  │ Transaction    │ │ ProvablyFair   │ │ Notification    │  │    │
│  │  │ Service        │ │ Service        │ │ Service         │  │    │
│  │  └────────────────┘ └────────────────┘ └─────────────────┘  │    │
│  └─────────────────────────────────────────────────────────────┘    │
│         │                                                            │
│         │  Interfaces (Repository Contracts)                         │
└─────────┼────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────┐
│                          DOMAIN LAYER                                │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │                         ENTITIES                             │    │
│  │  ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌──────────┐   │    │
│  │  │ User   │ │ Case   │ │ Battle │ │ Item   │ │Transaction│  │    │
│  │  └────────┘ └────────┘ └────────┘ └────────┘ └──────────┘   │    │
│  └─────────────────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────────────────┐    │
│  │                   REPOSITORY INTERFACES                      │    │
│  │  ┌────────────────┐ ┌────────────────┐ ┌─────────────────┐  │    │
│  │  │ IUserRepo      │ │ ICaseRepo      │ │ IBattleRepo     │  │    │
│  │  └────────────────┘ └────────────────┘ └─────────────────┘  │    │
│  └─────────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────────┐
│                      INFRASTRUCTURE LAYER                            │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐      │
│  │   Database      │  │     Redis       │  │  External APIs  │      │
│  │  Repositories   │  │   Services      │  │    Services     │      │
│  │  (Prisma)       │  │  (Cache/Lock)   │  │ (Steam/Payment) │      │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘      │
│  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐      │
│  │   WebSocket     │  │     Auth        │  │     Audit       │      │
│  │   Gateway       │  │   Services      │  │    Service      │      │
│  └─────────────────┘  └─────────────────┘  └─────────────────┘      │
└─────────────────────────────────────────────────────────────────────┘

Descrição das Camadas

1. Presentation Layer (Camada de Apresentação)

Responsabilidade: Receber requisições HTTP, validar entrada, e retornar respostas.

Componentes:

  • Controllers: Endpoints HTTP que delegam para Use Cases
  • Guards: Autenticação e autorização
  • Pipes: Validação e transformação de dados
  • Modules: Organização e injeção de dependências NestJS
  • DTOs: Data Transfer Objects para request/response

Regras:

  • ❌ NÃO contém lógica de negócio
  • ❌ NÃO acessa banco de dados diretamente
  • ✅ Apenas orquestra chamadas para Use Cases
  • ✅ Trata erros HTTP e formata respostas

2. Application Layer (Camada de Aplicação)

Responsabilidade: Orquestrar a lógica de negócio através de Use Cases.

Componentes:

  • Use Cases: Um caso de uso por operação de negócio
  • Application Services: Serviços compartilhados entre use cases
  • Interfaces: Contratos para repositórios e serviços externos

Regras:

  • ✅ Contém a lógica de negócio da aplicação
  • ✅ Coordena múltiplos repositórios/serviços
  • ❌ NÃO conhece HTTP, WebSocket ou detalhes de framework
  • ❌ NÃO depende de implementações concretas

3. Domain Layer (Camada de Domínio)

Responsabilidade: Definir entidades e regras de negócio puras.

Componentes:

  • Entities: Modelos de dados do domínio
  • Repository Interfaces: Contratos de acesso a dados
  • Domain Services: Lógica de domínio complexa (raro)

Regras:

  • ✅ Camada mais interna e estável
  • ✅ Zero dependências externas
  • ❌ NÃO conhece framework, banco de dados ou HTTP
  • ❌ NÃO muda por motivos técnicos

4. Infrastructure Layer (Camada de Infraestrutura)

Responsabilidade: Implementar detalhes técnicos e integrações.

Componentes:

  • Repositories: Implementação Prisma dos repositórios
  • External APIs: Integrações (Steam, pagamentos, S3)
  • Cache/Queue: Redis, BullMQ
  • Auth: Serviços de autenticação
  • WebSocket: Gateway Socket.io

Regras:

  • ✅ Implementa interfaces da camada de domínio
  • ✅ Contém código específico de tecnologia
  • ❌ NÃO contém lógica de negócio
  • ❌ Lógica de negócio NÃO deve depender desta camada

Fluxo de uma Requisição

Estrutura de Pastas

src/
├── domain/                    # Camada de Domínio
│   ├── entities/              # Entidades do domínio
│   │   ├── user.entity.ts
│   │   ├── case.entity.ts
│   │   ├── battle.entity.ts
│   │   └── ...
│   └── repositories/          # Interfaces dos repositórios
│       ├── user.repository.interface.ts
│       ├── case.repository.interface.ts
│       └── ...

├── application/               # Camada de Aplicação
│   ├── use-cases/             # Casos de uso
│   │   ├── case-opening/
│   │   │   ├── open-case.use-case.ts
│   │   │   └── verify-opening.use-case.ts
│   │   ├── battle/
│   │   │   ├── create-battle.use-case.ts
│   │   │   └── join-battle.use-case.ts
│   │   └── ...
│   ├── services/              # Serviços de aplicação
│   │   ├── transaction.service.ts
│   │   ├── provably-fair.service.ts
│   │   └── ...
│   ├── dto/                   # Data Transfer Objects
│   │   ├── auth.dto.ts
│   │   ├── case.dto.ts
│   │   └── ...
│   └── interfaces/            # Interfaces de serviços
│       ├── payment.interface.ts
│       └── storage.interface.ts

├── infrastructure/            # Camada de Infraestrutura
│   ├── database/
│   │   └── repositories/      # Implementações Prisma
│   │       ├── user.repository.ts
│   │       ├── case.repository.ts
│   │       └── ...
│   ├── auth/                  # Autenticação
│   │   ├── session.service.ts
│   │   └── steam-oauth.service.ts
│   ├── redis/                 # Cache e locks
│   │   ├── redis.service.ts
│   │   └── lock.service.ts
│   ├── websocket/             # Tempo real
│   │   └── websocket.gateway.ts
│   └── external-apis/         # APIs externas
│       ├── steam.service.ts
│       └── payment.service.ts

└── presentation/              # Camada de Apresentação
    ├── controllers/           # Controllers HTTP
    │   ├── auth.controller.ts
    │   ├── case.controller.ts
    │   └── ...
    ├── guards/                # Guards de autenticação
    │   ├── auth.guard.ts
    │   └── admin.guard.ts
    ├── pipes/                 # Pipes de validação
    │   └── validation.pipe.ts
    └── modules/               # Módulos NestJS
        ├── auth.module.ts
        ├── case.module.ts
        └── ...

Injeção de Dependências

O NestJS gerencia a injeção de dependências. Repositórios são injetados através de tokens:

typescript
// Definição do token
export const USER_REPOSITORY = 'USER_REPOSITORY';

// No módulo
@Module({
  providers: [
    {
      provide: USER_REPOSITORY,
      useClass: UserRepository,
    },
    GetUserUseCase,
  ],
})
export class UserModule {}

// No use case
@Injectable()
export class GetUserUseCase {
  constructor(
    @Inject(USER_REPOSITORY)
    private readonly userRepository: IUserRepository,
  ) {}
}

Benefícios Práticos

1. Testabilidade

typescript
// Use case pode ser testado com mock do repositório
describe('OpenCaseUseCase', () => {
  it('should open case and return item', async () => {
    const mockRepo = { findById: jest.fn().mockResolvedValue(mockCase) };
    const useCase = new OpenCaseUseCase(mockRepo);
    
    const result = await useCase.execute(userId, caseId);
    
    expect(result.item).toBeDefined();
  });
});

2. Substituibilidade

typescript
// Trocar implementação sem alterar lógica de negócio
@Module({
  providers: [
    {
      provide: PAYMENT_PROVIDER,
      useClass: process.env.PAYMENT === 'stripe' 
        ? StripeProvider 
        : PaagProvider,
    },
  ],
})

3. Organização Clara

Cada desenvolvedor sabe exatamente onde colocar cada tipo de código:

  • Endpoint novo? → Controller
  • Lógica de negócio? → Use Case
  • Query no banco? → Repository
  • Integração externa? → Infrastructure Service

Anti-patterns Evitados

O que NÃO fazer

1. Lógica de negócio no Controller

typescript
// ❌ ERRADO
@Post('open')
async openCase() {
  const roll = Math.random(); // Lógica no controller!
  if (roll > 0.5) { ... }
}

// ✅ CORRETO
@Post('open')
async openCase() {
  return this.openCaseUseCase.execute(userId, caseId);
}

2. Use Case acessando Request/Response HTTP

typescript
// ❌ ERRADO
class OpenCaseUseCase {
  execute(req: Request, res: Response) { ... }
}

// ✅ CORRETO
class OpenCaseUseCase {
  execute(userId: bigint, caseId: bigint) { ... }
}

3. Repository com lógica de negócio

typescript
// ❌ ERRADO
class UserRepository {
  async createWithBonus(data) {
    const bonus = data.isNew ? 100 : 0; // Lógica no repo!
    return this.prisma.user.create({ ...data, balance: bonus });
  }
}

// ✅ CORRETO - Lógica fica no Use Case
class CreateUserUseCase {
  execute(data) {
    const bonus = data.isNew ? 100 : 0;
    return this.userRepo.create({ ...data, balance: bonus });
  }
}

Arquivos Fonte Relacionados

Principais Arquivos

  • src/app.module.ts - Módulo raiz com imports de todas as camadas
  • src/domain/ - Entidades e interfaces
  • src/application/use-cases/ - Todos os use cases
  • src/infrastructure/database/repositories/ - Implementações dos repositórios
  • src/presentation/controllers/ - Todos os controllers

Documentação Técnica CSGOFlip