DO NOT COMMENT EXECESSIVELY. Instead, write clear and concise code that is self-explanatory.
iota-sdk is a general purpose ERP building engine/solution. When designing anything inside iota-sdk:
- Make it extensible, generalizable, and customizable
- Prefer interfaces over concrete structs at boundaries
- Apply dependency inversion and inject interfaces
- Keep domain and services decoupled from infrastructure details
Each module follows a strict Domain-Driven Design (DDD) pattern with clear layer separation:
modules/{module}/
├── domain/ # Pure business logic
│ ├── aggregates/{entity}/ # Complex business entities
│ │ ├── {entity}.go # Entity interface
│ │ ├── {entity}_impl.go # Entity implementation
│ │ ├── {entity}_events.go # Domain events
│ │ └── {entity}_repository.go # Repository interface
│ ├── entities/{entity}/ # Simpler domain entities
│ └── value_objects/ # Immutable domain concepts
├── infrastructure/ # External concerns
│ └── persistence/
│ ├── models/models.go # Database models
│ ├── {entity}_repository.go # Repository implementations
│ ├── {module}_mappers.go # Domain-to-DB mapping
│ ├── schema/{module}-schema.sql # SQL schema
│ └── setup_test.go # Test utilities
├── services/ # Business logic orchestration
│ ├── {entity}_service.go # Service implementation
│ ├── {entity}_service_test.go # Service tests
│ └── setup_test.go # Test setup
├── presentation/ # UI and API layer
│ ├── controllers/
│ │ ├── {entity}_controller.go # HTTP handlers
│ │ ├── {entity}_controller_test.go # Controller tests
│ │ ├── dtos/{entity}_dto.go # Data transfer objects
│ │ └── setup_test.go # Test utilities
│ ├── templates/
│ │ ├── pages/{entity}/ # Entity-specific pages
│ │ │ ├── list.templ # List view
│ │ │ ├── edit.templ # Edit form
│ │ │ └── new.templ # Create form
│ │ └── components/ # Reusable UI components
│ ├── viewmodels/ # Presentation models
│ ├── mappers/mappers.go # Domain-to-presentation mapping
│ └── locales/ # Internationalization
│ ├── en.json # English translations
│ ├── ru.json # Russian translations
│ └── uz.json # Uzbek translations
├── module.go # Module registration
├── links.go # Navigation items
└── permissions/constants.go # RBAC permissions
- Create domain entity in
modules/{module}/domain/aggregates/{entity_name}/ - Define repository interface with CRUD operations and domain events
- Follow existing patterns (see
payment_categoryorexpense_category)
- Add database model to
modules/{module}/infrastructure/persistence/models/models.go - Create repository implementation in
modules/{module}/infrastructure/persistence/{entity_name}_repository.go - Add domain-to-database mappers in
modules/{module}/infrastructure/persistence/{module}_mappers.go
- Create service in
modules/{module}/services/{entity_name}_service.go - Include event publishing and business logic methods
- Follow constructor pattern:
NewEntityService(repo, eventPublisher)
- Create DTOs in
modules/{module}/presentation/controllers/dtos/{entity_name}_dto.go - Create controller in
modules/{module}/presentation/controllers/{entity_name}_controller.go - Create viewmodel in
modules/{module}/presentation/viewmodels/{entity_name}_viewmodel.go - Add mapper in
modules/{module}/presentation/mappers/mappers.go
- Create templ files in
modules/{module}/presentation/templates/pages/{entity_name}/ - Common templates:
list.templ,edit.templ,new.templ - Run
templ generateafter creating/modifying .templ files
- Add translations to all locale files in
modules/{module}/presentation/locales/ - Include NavigationLinks, Meta (titles), List, and Single sections
- Add navigation item to
modules/{module}/links.go - Register the module in
modules/{module}/component.go:- Register repositories and services with
composition.ProvideFunc(...)— the reflection injector resolves each constructor parameter from the container by type (no manual lookup) - Use
composition.ProvideFuncAs[Interface]when a constructor returns a concrete type but you want the provider keyed by an interface - Add controllers with
composition.ContributeControllersFunc(...)orcomposition.AddControllers(...)for pre-built instances - Attach static contributions via
composition.AddNavItems,composition.AddQuickLinks,composition.AddHashFS - Declare locale files on the component itself via the required
LocaleFS()method; the engine wires them automatically.
- Register repositories and services with
- Run
go vet ./...to verify compilation - Run
templ generate && just cssif templates were modified
- Backend: Go 1.24.10, IOTA SDK framework, GraphQL
- Database: PostgreSQL 13+ (multi-tenant with organization_id)
- Frontend: HTMX + Alpine.js + Templ + Tailwind CSS
- Auth: Cookie-based sessions with RBAC
- Multi-tenant isolation: Always include
tenant_idin WHERE clauses - Error handling: Use
pkg/serrors-serrors.E(op, err)pattern - HTMX: Check
htmx.IsHxRequest(r), usehtmx.SetTrigger(w, "event", payload) - Never read
*_templ.gofiles - they're generated
- DO NOT USE
sedfor file manipulation - Prefer
mcp__bloom__search_code(repo: "iota-uz/iota-sdk")for semantic search when you do not know exact file names or need to explore the codebase
- Format Go code and templates:
just fix fmt - Organize and format Go imports:
just fix imports - Lint code (check unused variables/functions):
just check lint - Check translation files:
just check tr
- After changes to css or .templ files:
templ generate && just css - After changes to Go code:
go vet ./... - Never run
go build- usego vetinstead - Run all tests:
just test -vorgo test -v ./... - Run single test:
go test -v ./path/to/package -run TestName - Run specific subtest:
go test -v ./path/to/package -run TestName/SubtestName - Apply migrations:
just db migrate up
just e2e run # Interactive UI mode
just e2e ci # Headless CI mode
cd e2e && npx playwright test tests/module/specific.spec.ts # Single test- Use
go fmtfor formatting. Do not indent code manually. - Use Go v1.24.10 and follow standard Go idioms
- File organization: group related functionality in modules/ or pkg/ directories
- Naming: use camelCase for variables, PascalCase for exported functions/types
- Testing: table-driven tests with descriptive names (TestFunctionName_Scenario), use the
requireandassertpackages fromgithub.com/stretchr/testify - Error handling: use
pkg/serrorswith patternserrors.E(op, err)for standard error types - When writing a mapper function, always use utilities from
pkg/mappingto ensure consistency - Type safety: use strong typing and avoid interface{} where possible
- Follow existing patterns for database operations with jmoiron/sqlx
- For UI components, follow the existing templ/htmx patterns
- Use
pkg/htmxfor all UI interactions - Use existing components from
components/package before creating new ones
- Use
htmx.IsHxRequest(r)to check if a request is from HTMX - Use
htmx.SetTrigger(w, "eventName", payload)for setting HTMX response triggers