Skip to content

🏗️ Arquitetura do Sistema - FinBoost+

Microservices React Spring Boot PostgreSQL

Este documento apresenta a arquitetura técnica do FinBoost+, detalhando decisões de design, padrões utilizados e estrutura do sistema.


🎯 Visão Geral da Arquitetura

Arquitetura de Alto Nível

graph TB
    subgraph "Frontend"
        UI[React 19 + Vite 7]
        State[Context API]
        Router[React Router v6]
    end

    subgraph "Backend"
        API[Spring Boot 3.5]
        Security[Spring Security 6]
        JPA[Spring Data JPA]
    end

    subgraph "Database"
        DB[(PostgreSQL 15)]
    end

    subgraph "External"
        CDN[Vercel/Netlify]
        Deploy[Railway/Render]
    end

    UI --> API
    API --> DB
    UI --> CDN
    API --> Deploy

Princípios Arquiteturais

  • Separation of Concerns: Camadas bem definidas
  • Clean Architecture: Independência de frameworks
  • SOLID Principles: Código maintível e extensível
  • RESTful API: Comunicação padronizada
  • Responsive Design: Mobile-first approach

🎨 Frontend - React Architecture

Stack Tecnológico

Core:
  - React: 19.x (Latest)
  - TypeScript: 5.x
  - Vite: 7.x (Build tool)

Styling:
  - TailwindCSS: 4.x
  - Headless UI: 2.x
  - CSS Modules: Scoped styles

State Management:
  - Context API: Global state
  - React Hooks: Local state
  - Custom Hooks: Business logic

Routing:
  - React Router: v6.x
  - Protected Routes: Authentication
  - Lazy Loading: Code splitting

Testing:
  - Vitest: Test framework
  - React Testing Library: Component testing
  - MSW: API mocking

Estrutura de Pastas

src/
├── components/          # Componentes reutilizáveis
│   ├── ui/             # Atomic components
│   ├── forms/          # Formulários
│   ├── Layout/         # Layout components
│   └── [Feature]/      # Feature components
├── pages/              # Páginas/Rotas
├── hooks/              # Custom hooks
├── context/            # Context providers
├── services/           # API services
├── utils/              # Utility functions
├── types/              # TypeScript types
└── routes/             # Route configuration

Padrões de Componentes

// Atomic Design Pattern
// Atoms -> Molecules -> Organisms -> Templates -> Pages

// Component Structure
const Component = ({ prop1, prop2, ...props }) => {
  // Hooks
  const [state, setState] = useState()
  const customHook = useCustomHook()

  // Effects
  useEffect(() => {
    // Side effects
  }, [dependencies])

  // Handlers
  const handleAction = () => {
    // Action logic
  }

  // Render
  return (
    <div {...props}>
      {/* JSX */}
    </div>
  )
}

Gerenciamento de Estado

// Context API Pattern
const AppContext = createContext()

const AppProvider = ({ children }) => {
  const [state, setState] = useState(initialState)

  const actions = {
    updateState: (newState) => setState(newState),
    // More actions...
  }

  return (
    <AppContext.Provider value={{ state, ...actions }}>
      {children}
    </AppContext.Provider>
  )
}

// Custom Hook
const useApp = () => {
  const context = useContext(AppContext)
  if (!context) {
    throw new Error('useApp must be used within AppProvider')
  }
  return context
}

⚙️ Backend - Spring Boot Architecture

Stack Tecnológico

Core:
  - Java: 21 LTS
  - Spring Boot: 3.5.x
  - Spring Framework: 6.x

Data:
  - Spring Data JPA: 3.x
  - Hibernate: 6.x
  - PostgreSQL: 15.x
  - Flyway: Migration tool

Security:
  - Spring Security: 6.x
  - JWT: Authentication
  - BCrypt: Password hashing

Web:
  - Spring Web MVC: REST API
  - Jackson: JSON serialization
  - Validation: Bean validation

Testing:
  - JUnit: 5.x
  - Mockito: Mocking
  - TestContainers: Integration tests

Arquitetura em Camadas

src/main/java/
├── controller/         # REST Controllers
├── service/           # Business Logic
├── repository/        # Data Access Layer
├── model/            # JPA Entities
├── dto/              # Data Transfer Objects
├── config/           # Configuration classes
├── security/         # Security configuration
├── exception/        # Exception handling
└── util/             # Utility classes

Padrão de Controlador

@RestController
@RequestMapping("/api/v1/groups")
@RequiredArgsConstructor
@Validated
public class GroupController {

    private final GroupService groupService;

    @GetMapping
    public ResponseEntity<PagedResponse<GroupDTO>> getGroups(
            @PageableDefault Pageable pageable,
            Authentication authentication) {

        String userEmail = authentication.getName();
        PagedResponse<GroupDTO> groups = groupService.getUserGroups(userEmail, pageable);

        return ResponseEntity.ok(groups);
    }

    @PostMapping
    public ResponseEntity<ApiResponse<GroupDTO>> createGroup(
            @Valid @RequestBody CreateGroupRequest request,
            Authentication authentication) {

        String userEmail = authentication.getName();
        GroupDTO group = groupService.createGroup(request, userEmail);

        return ResponseEntity.status(HttpStatus.CREATED)
                .body(ApiResponse.success("Grupo criado com sucesso", group));
    }
}

Padrão de Serviço

@Service
@RequiredArgsConstructor
@Transactional
public class GroupService {

    private final GroupRepository groupRepository;
    private final UserRepository userRepository;
    private final GroupMapper groupMapper;

    public PagedResponse<GroupDTO> getUserGroups(String userEmail, Pageable pageable) {
        User user = userRepository.findByEmail(userEmail)
                .orElseThrow(() -> new UserNotFoundException("Usuário não encontrado"));

        Page<Group> groups = groupRepository.findByMembersContaining(user, pageable);

        List<GroupDTO> groupDTOs = groups.getContent().stream()
                .map(groupMapper::toDTO)
                .collect(Collectors.toList());

        return PagedResponse.of(groupDTOs, groups);
    }

    @Transactional
    public GroupDTO createGroup(CreateGroupRequest request, String userEmail) {
        User creator = userRepository.findByEmail(userEmail)
                .orElseThrow(() -> new UserNotFoundException("Usuário não encontrado"));

        Group group = Group.builder()
                .name(request.getName())
                .description(request.getDescription())
                .inviteCode(generateInviteCode())
                .createdBy(creator)
                .build();

        group.addMember(creator);

        Group savedGroup = groupRepository.save(group);

        return groupMapper.toDTO(savedGroup);
    }
}

Configuração de Segurança

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    private final JwtAuthenticationFilter jwtAuthenticationFilter;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.cors(Customizer.withDefaults())
            .csrf(csrf -> csrf.disable())
            .sessionManagement(session -> 
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/v1/auth/**").permitAll()
                .requestMatchers("/api/v1/health").permitAll()
                .anyRequest().authenticated())
            .exceptionHandling(ex -> 
                ex.authenticationEntryPoint(jwtAuthenticationEntryPoint))
            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

🗄️ Banco de Dados - PostgreSQL

Modelo de Dados

-- Principais entidades e relacionamentos

CREATE TABLE users (
    id BIGSERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    avatar_url VARCHAR(500),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE groups (
    id BIGSERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    description TEXT,
    invite_code VARCHAR(10) UNIQUE NOT NULL,
    created_by BIGINT REFERENCES users(id),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE group_members (
    id BIGSERIAL PRIMARY KEY,
    group_id BIGINT REFERENCES groups(id) ON DELETE CASCADE,
    user_id BIGINT REFERENCES users(id) ON DELETE CASCADE,
    is_admin BOOLEAN DEFAULT FALSE,
    joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(group_id, user_id)
);

CREATE TABLE expenses (
    id BIGSERIAL PRIMARY KEY,
    group_id BIGINT REFERENCES groups(id) ON DELETE CASCADE,
    description VARCHAR(200) NOT NULL,
    amount DECIMAL(10,2) NOT NULL,
    category VARCHAR(50) NOT NULL,
    date DATE NOT NULL,
    notes TEXT,
    created_by BIGINT REFERENCES users(id),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE expense_splits (
    id BIGSERIAL PRIMARY KEY,
    expense_id BIGINT REFERENCES expenses(id) ON DELETE CASCADE,
    user_id BIGINT REFERENCES users(id),
    amount DECIMAL(10,2) NOT NULL,
    percentage DECIMAL(5,2) NOT NULL,
    UNIQUE(expense_id, user_id)
);

Índices e Performance

-- Índices para performance
CREATE INDEX idx_groups_created_by ON groups(created_by);
CREATE INDEX idx_group_members_group_id ON group_members(group_id);
CREATE INDEX idx_group_members_user_id ON group_members(user_id);
CREATE INDEX idx_expenses_group_id ON expenses(group_id);
CREATE INDEX idx_expenses_created_by ON expenses(created_by);
CREATE INDEX idx_expenses_date ON expenses(date);
CREATE INDEX idx_expense_splits_expense_id ON expense_splits(expense_id);
CREATE INDEX idx_expense_splits_user_id ON expense_splits(user_id);

🔄 Integração e APIs

Padrão de Response

{
  "success": true,
  "message": "Operação realizada com sucesso",
  "data": {
    // Dados da resposta
  },
  "pagination": {
    "page": 0,
    "size": 20,
    "totalElements": 100,
    "totalPages": 5
  },
  "timestamp": "2024-01-16T10:30:00Z"
}

Tratamento de Erros

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Dados inválidos",
    "details": [
      {
        "field": "email",
        "message": "Email é obrigatório"
      }
    ]
  },
  "timestamp": "2024-01-16T10:30:00Z",
  "path": "/api/v1/groups"
}

Versionamento de API

  • URL Versioning: /api/v1/, /api/v2/
  • Backward Compatibility: Manter versões anteriores
  • Deprecation Policy: 6 meses de notice

🚀 Deploy e Infraestrutura

Frontend (Vercel/Netlify)

Build:
  - Framework: Vite
  - Output: dist/
  - Commands: npm run build
  - Node: 18.x

Environment:
  - VITE_API_URL: Backend URL
  - VITE_APP_VERSION: Application version

Features:
  - CDN: Global distribution
  - SSL: Automatic HTTPS
  - Cache: Static assets
  - Preview: PR previews

Backend (Railway/Render)

Runtime:
  - Java: 21
  - Memory: 512MB
  - CPU: 0.5 vCPU

Database:
  - PostgreSQL: 15.x
  - Connection Pool: HikariCP
  - Migrations: Flyway

Environment:
  - SPRING_PROFILES_ACTIVE: production
  - DATABASE_URL: Connection string
  - JWT_SECRET: Secret key

CI/CD Pipeline

# GitHub Actions
name: Deploy

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      - name: Run tests
        run: npm test

  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to Vercel
        uses: amondnet/vercel-action@v20

📊 Monitoramento e Observabilidade

Métricas de Performance

  • Frontend: Core Web Vitals, Bundle size
  • Backend: Response time, Throughput, Error rate
  • Database: Query performance, Connection pool

Logging

@Slf4j
@Service
public class GroupService {

    public GroupDTO createGroup(CreateGroupRequest request, String userEmail) {
        log.info("Creating group: {} for user: {}", request.getName(), userEmail);

        try {
            // Business logic
            log.info("Group created successfully: {}", group.getId());
            return groupMapper.toDTO(group);
        } catch (Exception e) {
            log.error("Error creating group for user: {}", userEmail, e);
            throw e;
        }
    }
}

Health Checks

@RestController
public class HealthController {

    @Autowired
    private DataSource dataSource;

    @GetMapping("/api/v1/health")
    public ResponseEntity<Map<String, String>> health() {
        Map<String, String> status = new HashMap<>();
        status.put("status", "UP");
        status.put("database", checkDatabase());
        status.put("timestamp", Instant.now().toString());

        return ResponseEntity.ok(status);
    }
}

🔒 Segurança

Autenticação e Autorização

  • JWT Tokens: Stateless authentication
  • Password Hashing: BCrypt with salt
  • Role-based Access: Admin/Member permissions
  • Session Management: Token expiration

Proteções Implementadas

  • CORS: Cross-origin requests
  • CSRF: Cross-site request forgery
  • SQL Injection: Prepared statements
  • XSS: Input sanitization
  • Rate Limiting: API throttling

📈 Escalabilidade

Estratégias de Crescimento

  • Horizontal Scaling: Multiple instances
  • Database Optimization: Indexing, query tuning
  • Caching: Redis for sessions/data
  • CDN: Static asset distribution
  • Load Balancing: Traffic distribution

Futuras Melhorias

  • Microservices: Service decomposition
  • Event Sourcing: Audit trail
  • CQRS: Command/Query separation
  • GraphQL: Flexible API queries

🏗️ Arquitetura sólida e escalável
Base técnica para crescimento sustentável