Skip to main content

Project Structure

Panduan lengkap struktur folder dan file untuk memahami organisasi codebase.

📁 Root Structure

project-root/
├── backend/              # Go services
├── frontend/             # Flutter app
├── docs/                 # Documentation (Mintlify)
├── docker-compose.yml    # Dev services
├── Taskfile.yml          # Task definitions
├── .env.example          # Environment template
├── .gitignore
├── README.md
└── observability/        # LGTM configs

🔧 Backend Structure (Go)

backend/
├── cmd/
│   └── api/              # API Gateway
│       └── main.go

├── internal/
│   ├── route/            # 7-Domain Route Architecture
│   │   ├── route.go      # Main router entry point
│   │   ├── v1/           # API V1 (Backward Compatible)
│   │   │   ├── route_registry.go
│   │   │   ├── core/
│   │   │   │   └── core_route.go
│   │   │   ├── ops/
│   │   │   │   ├── transaction_route.go
│   │   │   │   ├── inventory_route.go
│   │   │   │   └── pos_route.go
│   │   │   ├── support/
│   │   │   │   ├── finance_route.go
│   │   │   │   ├── hr_route.go
│   │   │   │   └── crm_route.go
│   │   │   ├── webhook/
│   │   │   │   └── webhook_route.go
│   │   │   ├── analytics/
│   │   │   ├── holding/
│   │   │   └── global/
│   │   └── v2/           # API V2 (Enhanced)
│   │       ├── route_registry.go
│   │       ├── core/
│   │       ├── ops/
│   │       ├── support/
│   │       ├── governance/
│   │       ├── analytics/
│   │       ├── holding/
│   │       └── global/
│   │
│   ├── domains/          # Business domains (services)
│   │   ├── auth/         # Auth domain
│   │   │   ├── handler/
│   │   │   ├── service/
│   │   │   ├── repository/
│   │   │   ├── model/
│   │   │   └── dto/
│   │   ├── transaction/
│   │   ├── inventory/
│   │   ├── pos/
│   │   ├── webhook/
│   │   └── ...
│   │
│   ├── middleware/       # HTTP middleware
│   │   ├── auth_middleware.go
│   │   ├── rbac_middleware.go
│   │   ├── audit_middleware.go
│   │   └── ...
│   │
│   ├── store/            # Handler registry
│   │   └── handler.go
│   │
│   └── shared/           # Shared packages
│       ├── config/       # Configuration
│       ├── database/     # DB connections
│       ├── logger/       # Logging
│       ├── validator/    # Validation
│       ├── errors/       # Error handling
│       └── utils/        # Utilities

├── pkg/                  # Public packages
│   ├── common/
│   ├── utils/
│   └── response/

├── config/               # Config files
│   ├── api/
│   │   ├── endpoints-registry-v1.yaml
│   │   └── endpoints-registry-v2.yaml
│   ├── rbac/
│   │   ├── policy.csv
│   │   └── sod_rules.yaml
│   └── ...

├── migrations/           # Database migrations
│   ├── atlas/
│   │   └── schema/
│   └── ...

├── tests/                # Tests
│   ├── integration/
│   ├── e2e/
│   └── fixtures/

├── .air.toml             # Air config (live reload)
├── go.mod
├── go.sum
├── Dockerfile
└── README.md

Backend File Examples

  • main.go
  • handler.go
  • service.go
  • repository.go
// cmd/api/main.go
package main

import (
    "context"
    "log"
    "os"
    "os/signal"
    "syscall"
    
    "github.com/your/project/internal/shared/config"
    "github.com/your/project/internal/shared/database"
    "github.com/your/project/internal/shared/logger"
)

func main() {
    // Load config
    cfg, err := config.Load()
    if err != nil {
        log.Fatal(err)
    }
    
    // Setup logger
    logger := logger.New(cfg.Log)
    
    // Connect to database
    db, err := database.NewMySQL(cfg.Database)
    if err != nil {
        logger.Fatal("failed to connect database", err)
    }
    defer db.Close()
    
    // Setup server
    server := setupServer(cfg, db, logger)
    
    // Graceful shutdown
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    
    go func() {
        if err := server.Start(); err != nil {
            logger.Fatal("server error", err)
        }
    }()
    
    <-quit
    logger.Info("shutting down server...")
    
    if err := server.Shutdown(context.Background()); err != nil {
        logger.Fatal("server shutdown error", err)
    }
}

🧩 7-Domain Route Architecture

MStore Backend menggunakan 7-Domain Driven Architecture untuk route organization:

Domain Hierarchy

CORE (Foundation)

OPS (Operational)

SUPPORT (Backoffice)

GOVERNANCE (Control)

ANALYTICS (Insight)

HOLDING (Multi-Entity)

GLOBAL (Infrastructure)

V1 Routes (Backward Compatible)

DomainEndpointsStatus
CORE21✅ Auth, User, Merchant, Branches, Roles
OPS37✅ Stok, Transactions, POS, Inventory
SUPPORT0📋 Placeholder (Finance, HR, CRM)
WEBHOOK1✅ Xendit Payments

V2 Routes (Enhanced)

DomainEndpointsStatus
CORE7✅ Auth, Role, System
OPS20✅ POS, Sales, Procurement, Inventory
SUPPORT20✅ Finance, HR, CRM, Marketing, CS
GOVERNANCE7✅ Audit, Dashboard, Holding, Global
ANALYTICS6✅ Dashboard, BI, Forecast
HOLDING10✅ Entity, Consolidation, Intercompany
GLOBAL12✅ IAM, Security, Data Governance, ESG
Total: 82 endpoints

Adding New Routes

  1. Create domain folder: internal/route/v1/your-domain/
  2. Create route file: your_domain_route.go
  3. Update route_registry.go with import and call
  4. Build and test
See: 7-Domain Route Architecture

📱 Frontend Structure (Flutter)

frontend/
├── lib/
│   ├── main.dart
│   │
│   ├── core/                 # Core functionality
│   │   ├── config/           # App configuration
│   │   │   ├── env.dart
│   │   │   └── theme.dart
│   │   ├── constants/        # Constants
│   │   │   ├── api_constants.dart
│   │   │   └── app_constants.dart
│   │   ├── di/               # Dependency injection
│   │   │   └── injection.dart
│   │   ├── network/          # Network layer
│   │   │   ├── dio_client.dart
│   │   │   ├── interceptors/
│   │   │   └── api_response.dart
│   │   ├── router/           # Navigation
│   │   │   └── app_router.dart
│   │   └── utils/            # Utilities
│   │       ├── logger.dart
│   │       └── validators.dart
│   │
│   ├── features/             # Feature modules
│   │   ├── auth/
│   │   │   ├── data/
│   │   │   │   ├── models/
│   │   │   │   ├── datasources/
│   │   │   │   └── repositories/
│   │   │   ├── domain/
│   │   │   │   ├── entities/
│   │   │   │   ├── repositories/
│   │   │   │   └── usecases/
│   │   │   └── presentation/
│   │   │       ├── pages/
│   │   │       ├── widgets/
│   │   │       └── providers/
│   │   │
│   │   ├── home/
│   │   │   ├── data/
│   │   │   ├── domain/
│   │   │   └── presentation/
│   │   │
│   │   ├── product/
│   │   │   ├── data/
│   │   │   ├── domain/
│   │   │   └── presentation/
│   │   │
│   │   └── order/
│   │       ├── data/
│   │       ├── domain/
│   │       └── presentation/
│   │
│   └── shared/               # Shared widgets & utils
│       ├── widgets/
│       │   ├── buttons/
│       │   ├── inputs/
│       │   ├── cards/
│       │   └── dialogs/
│       ├── extensions/
│       └── mixins/

├── test/                     # Tests
│   ├── unit/
│   ├── widget/
│   └── integration/

├── assets/                   # Assets
│   ├── images/
│   ├── icons/
│   └── fonts/

├── pubspec.yaml
├── analysis_options.yaml
└── README.md

Flutter File Examples

  • main.dart
  • dio_client.dart
  • provider.dart
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'core/config/theme.dart';
import 'core/di/injection.dart';
import 'core/router/app_router.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // Setup dependencies
  await setupDependencies();
  
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}

class MyApp extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final router = ref.watch(routerProvider);
    
    return MaterialApp.router(
      title: 'Windsurf App',
      theme: AppTheme.lightTheme,
      darkTheme: AppTheme.darkTheme,
      routerConfig: router,
    );
  }
}

🐳 Docker & DevOps

docker-compose.yml          # Dev services
Dockerfile                  # App container
.dockerignore

observability/
├── grafana/
│   ├── dashboards/
│   │   ├── api-performance.json
│   │   └── system-metrics.json
│   └── provisioning/
│       ├── datasources/
│       └── dashboards/

├── loki/
│   └── loki-config.yaml

├── tempo/
│   └── tempo-config.yaml

├── mimir/
│   └── mimir-config.yaml

└── promtail/
    └── promtail-config.yaml

📚 Documentation

docs/
├── 00-getting-started/
├── 10-architecture/
├── 20-backend-go/
├── 30-frontend-flutter/
├── 40-database/
├── 50-observability-lgtm/
├── 60-devops-go/
├── 70-ci-cd/
├── 80-guides/
├── 90-features/
├── 95-technical-specs/
└── 99-changelog/

mint.json                   # Mintlify config

🔑 Key Files

Environment Files

  • .env.example
  • Taskfile.yml
  • .air.toml
# Application
APP_ENV=development
APP_NAME=windsurf-app
APP_PORT=8080

# Database
MYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_USER=root
MYSQL_PASSWORD=secret
MYSQL_DATABASE=windsurf_db

MONGO_URI=mongodb://localhost:27017
MONGO_DATABASE=windsurf_mongo

# Redis
REDIS_HOST=localhost
REDIS_PORT=6379

# JWT
JWT_SECRET=your-secret-key
JWT_EXPIRY=24h

📊 Naming Conventions

Backend (Go)

  • Packages: lowercase, single word (handler, service, repository)
  • Files: snake_case (user_handler.go, auth_service.go)
  • Types: PascalCase (UserHandler, AuthService)
  • Functions: camelCase (exported: NewUserHandler, private: validateInput)
  • Constants: UPPER_SNAKE_CASE atau PascalCase
  • Interfaces: PascalCase dengan suffix (e.g., UserRepository, AuthService)

Frontend (Flutter)

  • Files: snake_case (login_page.dart, user_model.dart)
  • Classes: PascalCase (LoginPage, UserModel)
  • Variables: camelCase (userName, isLoading)
  • Constants: lowerCamelCase atau UPPER_SNAKE_CASE
  • Private: prefix underscore (_privateMethod, _PrivateClass)

🎯 Best Practices

  • Gunakan Clean Architecture (handler → service → repository)
  • Pisahkan domain logic dari infrastructure
  • Setiap service punya folder sendiri di internal/
  • Shared code di internal/shared/ atau pkg/
  • Test files sejajar dengan source files (*_test.go)
  • Gunakan Feature-First structure
  • Setiap feature punya layer: data, domain, presentation
  • Shared widgets di shared/widgets/
  • State management dengan Riverpod
  • Dependency injection di core/di/
  • Naming: {version}_{description}.{up|down}.sql
  • Selalu buat up & down migration
  • Test migration sebelum commit
  • Jangan edit migration yang sudah di-deploy
  • Environment variables untuk secrets
  • YAML files untuk app config
  • Jangan commit .env file
  • Provide .env.example sebagai template

📚 Next Steps


Pro Tip: Gunakan VS Code workspace atau GoLand multi-project untuk membuka backend dan frontend secara bersamaan!