Clean Architecture + Dependency Injection (Wire) implementation for collecting real-time market data from Finnhub and streaming to Kafka/ClickHouse.
pkg/ # Shared infrastructure layer
├── server/app.go → Application lifecycle (Start/Stop/Health/Ready)
├── clickhouse/client.go → Connection pool manager + schema initialization
├── kafka/client.go → Writer manager + batch/compression config
├── config/config.go → YAML config loader + validation
└── metrics/prometheus.go → Prometheus metrics recorder
internal/
├── domain/
│ ├── models/trade.go → Business entities
│ └── repository/ → Port interfaces (abstractions)
│ └── interfaces.go → MarketStream, Publisher, Storage, Metrics
├── repository/ → Adapters (interface implementations)
│ ├── clickhouse/storage.go → ClickHouse storage adapter
│ └── kafka/publisher.go → Kafka publisher adapter
├── service/
│ └── finnhub/client.go → Finnhub WebSocket client (third-party)
├── usecase/ → Business logic
│ ├── trade_processor.go → Process & route trades to backend
│ └── trade_collector.go → Collect trades from market stream
└── di/ → Dependency injection
├── providers.go → Provider functions for Wire
└── wire.go → Wire injector definition
cmd/app/main.go → Entry point (3-step: load config → wire DI → run)
- Domain layer: Pure business logic, no external dependencies
- Use case layer: Application-specific business rules
- Interface adapters: Convert data between domain and external systems
- Infrastructure: Frameworks, tools, drivers (ClickHouse, Kafka, HTTP)
- All dependencies injected via Wire
- No global state or singletons
- Easy to test and mock
- pkg/: Infrastructure setup (connections, configs) - REUSABLE
- internal/: Application logic (domain, usecases, adapters) - BUSINESS SPECIFIC
- cmd/: Entry points - MINIMAL (3 steps)
- Connections created ONCE in
pkg/ - Repositories receive connections, never create them
- Cleanup handled centrally
make build
# or
go build -o bin/finpull ./cmd/app# Development
make run
# or
./bin/finpull -config config/dev.yaml
# Production
./bin/finpull -config config/production.yamlconfig/dev.yaml:
environment: development
backend:
type: kafka # or clickhouse
finnhub:
api_key: ${FINNHUB_API_KEY}
symbols:
- BINANCE:BTCUSDT
kafka:
brokers:
- kafka-headless:9092
topic: finpullEnvironment variables override YAML:
export FINNHUB_API_KEY=your_key
export BACKEND=clickhouse
./bin/finpullmake install-toolsInstalls:
- Wire (dependency injection code generator)
- golangci-lint (linter)
make wireGenerates internal/di/wire_gen.go.
make lintEnforces:
- Cyclomatic complexity < 15
- Cognitive complexity < 15
- Function length < 100 lines
- Error handling
- Code style
make testmake docker-upStarts:
- ClickHouse (
:8123,:9000) - Kafka (
:9092) - Zookeeper
- Postgres (
:5432) - Redis (
:6379) - Superset (
:8088) - Prometheus (
:9090) - Grafana (
:3000)
make docker-down- Health:
GET /health- Infrastructure health (ClickHouse + Kafka) - Ready:
GET /ready- Application readiness (Collector connected) - Metrics:
GET /metrics- Prometheus metrics
finpull_messages_sent_total{backend,symbol}- Total messages sentfinpull_errors_total{type}- Total errorsfinpull_last_price{symbol}- Last recorded pricefinpull_operation_duration_seconds{operation}- Operation latency
pkg/ - Infrastructure utilities (reusable across projects):
- Connection managers (DB, Kafka, Redis)
- Configuration loaders
- HTTP servers
- Metrics recorders
- Logging utilities
internal/ - Application-specific logic:
- Domain models and interfaces
- Business logic (use cases)
- Interface adapters (repositories, services)
- Dependency injection setup
cmd/ - Executable entry points:
- Parse flags
- Load config
- Wire dependencies
- Run application
- That's it. No business logic.
❌ DO NOT create connections in repositories
✅ DO inject connections from pkg/
❌ DO NOT put business logic in cmd/
✅ DO keep cmd/ minimal (3 steps)
❌ DO NOT import internal/ from pkg/
✅ DO keep pkg/ independent and reusable
❌ DO NOT use global variables
✅ DO inject dependencies via Wire
MIT
- Follow Clean Architecture principles
- Run
make lintbefore commit - Keep cyclomatic complexity < 15
- Add tests for new features
- Update docs
Built with: Go 1.24 | Wire | ClickHouse | Kafka | Prometheus | Grafana