SLA Policy Evaluator Structure Rules (Prompt-Ready)
Dokumen ini adalah standar struktur folder dan tanggung jawab file untuk apps/worker-workflow/src/sla-policy-evaluator.
Tujuan:
- menjaga struktur tetap konsisten dan mudah diikuti tim
- memudahkan refactor tanpa mengubah behavior runtime
- menjadi prompt/aturan kerja saat membuat context baru
1. Prinsip Utama
Gunakan pola:
context-firstflat per context(hindari nestedadapters/,application/,domain/,infrastructure/di dalam context)nama file menjelaskan peran
Konsep yang dipakai:
consumer= adapter transport (NATS/JetStream,ack/nak)service= use-case utama per context atau service pendukung reusablechecker/support= service pendukung reusable internal checkercontracts= boundary agreement (type,interface, DTO, abstract contract)- custom decorator JetStream:
@JetStreamConsumer(...)= metadata subscribe (class-level)@MessageHandler()= penanda method handler message (method-level)
2. Struktur Folder Standar
apps/worker-workflow/src/sla-policy-evaluator/
├── sla-policy-evaluator.module.ts
├── constants/
├── contracts/
├── docs/
├── services/
│ ├── cdc-forwarder/
│ ├── transition/
│ ├── schedule/
│ └── checker/
│ └── support/
└── utils/
Aturan:
services/cdc-forwarder,services/transition,services/schedule,services/checker= sub-context/flow runtimeservices/checker/support= service pendukung internal checkercontractstetap di root (bukan diservices)utilstetap di root untuk pure function
3. Tanggung Jawab Tiap Folder
3.1 constants/
- Konstanta stream subject, consumer group, token literal global context SLA Policy Evaluator
- Tidak berisi logic runtime
3.2 contracts/
- Boundary agreement antar komponen/context
- Boleh berisi:
typeinterface- DTO
- abstract contract/port (jika memang dipakai)
- Tidak boleh berisi:
@Injectable()- query DB
- business logic
Contoh:
sla-check.contract.tssla-schedule.contract.ts
3.3 docs/
- Dokumen behavior, refactor plan, study notes
- Bukan source runtime
3.4 services/cdc-forwarder/
- Semua flow adapter CDC dari
wxl_submission_logke payload SLA - Termasuk:
sla-cdc-forwarder.consumer.ts
3.5 services/transition/
- Semua consumer runtime topic SLA
- Termasuk:
- transition-check consumer
- aging consumer
3.6 services/schedule/
- Semua flow delay dispatcher topic schedule
- Termasuk:
- schedule consumer
3.7 services/checker/
- Main service/use-case evaluator SLA
- Termasuk:
sla-checker.service.tssla-transition-check.service.tssla-aging.service.ts
3.8 services/checker/support/
- Service reusable pendukung checker
- Contoh:
- transaction helper
- query helper (TypeORM)
- lifecycle instance SLA
- outcome policy executor
- event publisher
- policy matcher
3.9 utils/
- Pure function/helper tanpa DI (
@Injectable) - Tidak akses DB/NATS
- Tidak menyimpan state
4. Aturan Naming File
Gunakan suffix untuk menjelaskan peran:
-
*.consumer.ts- adapter transport
- contoh:
sla-aging.consumer.ts
-
*.service.ts- use-case utama context (mis.
sla-aging.service.ts) atau reusable supporting service (services/checker/support) - contoh:
sla-transition-check.service.ts,sla-instance-query.service.ts
- use-case utama context (mis.
-
*.contract.ts- boundary contract (
type/interface/DTO) - contoh:
sla-check.contract.ts
- boundary contract (
-
*.spec.ts- test colocated dengan file runtime (jangan pindah ke folder test global)
-
*.types.ts- tempatkan di
contracts/internal/untuk internal reusable types (non-boundary) - jangan taruh
*.types.tsdi dalamservices/*
- tempatkan di
5. Pembagian Tanggung Jawab File (Penting)
5.1 consumer (adapter transport)
Boleh:
- deklarasi metadata subscribe via
@JetStreamConsumer(...) - decode payload
- validasi payload minimum
ack/nak- memanggil service use-case context (
*.service.ts)
Tidak boleh:
- query DB kompleks
- business rule SLA yang panjang
- orchestration multi-step yang sulit dibaca
Catatan pola current SLA Policy Evaluator:
- bootstrap/registrasi consumer dilakukan oleh
JetStreamConsumerRegistryService waitUntilReady()dijalankan oleh registry (berdasarkan metadata consumer)consumertetap memegang manualack/nakdi method handler (jangan disembunyikan)
5.2 service (di context)
Boleh:
- flow bisnis/use-case end-to-end
- keputusan proses (resolve/start/breach/reschedule)
- memanggil
services/checker/support/*secara langsung (query/lifecycle/outcome/publisher)
Tidak boleh:
- transport concern (
ack/nak/subscribe) - menjadi "god service" lintas semua context
5.3 services/checker/support/*
Boleh:
- query helper reusable
- lifecycle service reusable
- publisher reusable
- transaction wrapper reusable
Tidak boleh:
- flow spesifik satu context jika tidak reusable
6. Aturan Penempatan Type (Hitam Putih)
Gunakan aturan ini tanpa asumsi:
- Boundary payload/event lintas context/module ->
contracts/*.contract.ts - Type internal reusable (bukan boundary) ->
contracts/internal/*.types.ts - Type yang hanya dipakai di test -> tetap di file
*.spec.ts - Hindari menaruh top-level
type/interfacedi file runtime (*.service.ts,*.consumer.ts)
Contoh:
SlaTransitionCheckPayload->contracts/sla-check.contract.tsSlaBreachPublishPayload->contracts/sla-outcome.contract.tsStartPolicyInstanceResult->contracts/internal/sla-instance-lifecycle.types.ts
7. Kapan File Masuk checker/support vs Context
Masuk services/checker/support jika:
- dipakai minimal 2 context
- sifatnya helper/service reusable
- bukan transport adapter
Tetap di context jika:
- hanya dipakai context itu
- logic sangat spesifik ke flow context tersebut
- naming akan lebih jelas bila dekat dengan flow
Rule praktis:
- kalau ragu, taruh di context dulu
- pindahkan ke
checker/supportsetelah benar-benar reusable
8. Pola Custom Decorator Untuk Consumer (Standar Saat Ini)
Gunakan pola ini untuk consumer JetStream baru agar konsisten:
- Class consumer diberi
@JetStreamConsumer({...}) - Method penerima message diberi
@MessageHandler() - Method handler melakukan:
safeDecode(...)- validasi minimum
- manual
ack/nak - call context service (
*.service.ts)
- Jangan implement
OnApplicationBootstrap/OnModuleDestroydi consumer yang sudah memakai registry
8.1 Contoh (ringkas)
import {
JetStreamConsumer,
MessageHandler,
} from '@lib/service/nats/jetstream-consumer/jetstream-consumer.decorator';
import { JetStreamConsumableMessage } from '@lib/service/nats/jetstream-consumer/jetstream-consumer.types';
import { Injectable } from '@nestjs/common';
import { consumerOpts } from 'nats';
@JetStreamConsumer({
name: 'SLA Aging Checker',
subject: String(SLA_AGING_CHECK_CONSUMER.SUBJECT),
waitUntilReady: true,
unhandledErrorNakDelayMs: 8000,
buildOpts: () =>
consumerOpts()
.durable(SLA_AGING_CHECK_CONSUMER.DURABLE)
.deliverGroup(SLA_AGING_CHECK_CONSUMER.GROUP)
.deliverTo(SLA_AGING_CHECK_CONSUMER.DELIVER)
.manualAck()
.ackExplicit()
.deliverNew()
.maxDeliver(SLA_AGING_CHECK_CONSUMER.MAX_REDELIVERY),
})
@Injectable()
export class SlaAgingConsumerAdapter {
constructor(private readonly agingService: SlaAgingService) {}
@MessageHandler()
async handleMessage(msg: JetStreamConsumableMessage) {
try {
const decoded = this.safeDecode(msg.data);
if (!decoded) {
await msg.ack();
return;
}
await this.agingService.handle(decoded);
await msg.ack();
} catch {
await msg.nak(8000);
}
}
}
8.2 Kenapa begini (rule hitam-putih)
- metadata subscribe berada di decorator (rapi, konsisten)
consumerOpts()chain tetap terlihat di file consumer (readable)ack/naktetap terlihat di file consumer (debuggable)waitUntilReadytetap ada, tetapi dijalankan registry (bukan tiap consumer copy-paste)
8.3 Registry yang wajib tersedia di module
- import
DiscoveryModule(@nestjs/core) - register
JetStreamConsumerRegistryServicesebagai provider module
9. Aturan Refactor (Safety Rules)
Saat refactor struktur:
- utamakan behavior-preserving
- ubah struktur/naming dulu, jangan ubah logic bisnis sekaligus
- setelah rename/move:
- update import path
- jalankan typecheck
- jalankan test
sla-policy-evaluator
Checklist minimum:
pnpm exec tsc -p apps/worker-workflow/tsconfig.json --noEmitpnpm exec jest $(rg --files apps/worker-workflow/src/sla-policy-evaluator -g '*.spec.ts') packages/lib/src/service/nats/jetstream-consumer/jetstream-consumer-registry.service.spec.ts --runInBand
10. Template Context Baru (Standar)
Jika menambah context baru (contoh replay), gunakan template:
services/replay/
├── README.md
├── sla-replay.consumer.ts # jika ada transport (prefer pakai decorator JetStream standar)
├── sla-replay.service.ts # use-case utama context
├── replay.query.ts # optional (query spesifik context)
├── sla-replay.consumer.spec.ts
└── sla-replay.service.spec.ts
Jika context sangat kecil:
- boleh hanya
consumer + spec - jangan memaksa ada service tambahan kalau logic masih trivial
11. Prompt Refactor (Siap Pakai)
Gunakan teks ini sebagai prompt untuk menjaga konsistensi:
Refactor folder/file di `apps/worker-workflow/src/sla-policy-evaluator` mengikuti aturan:
- context-first
- sub-context runtime berada di `services/` (`cdc-forwarder`, `transition`, `schedule`, `checker`, dst)
- reusable supporting services berada di `services/checker/support`
- boundary types/interfaces/DTO berada di `contracts/*.contract.ts`
- transport adapter memakai suffix `*.consumer.ts`
- jika consumer memakai JetStream:
- gunakan `@JetStreamConsumer(...)` pada class
- gunakan `@MessageHandler()` pada method handler
- simpan `consumerOpts()` chain di file consumer
- `ack/nak` tetap manual di method handler
- use-case utama context memakai suffix `*.service.ts`
- test colocated (`*.spec.ts`)
- jangan ubah behavior runtime kecuali diminta
Setelah refactor:
1. update seluruh import path
2. jalankan typecheck worker-workflow
3. jalankan seluruh test `sla-policy-evaluator`
4. laporkan file yang dipindah/di-rename dan risiko perubahan
12. Prompt Create Context Baru (Siap Pakai)
Buat context baru di `apps/worker-workflow/src/sla-policy-evaluator/services/<context-name>` dengan standar SLA Policy Evaluator:
- `README.md`
- `*.consumer.ts` jika ada transport message
- jika consumer JetStream:
- gunakan `@JetStreamConsumer(...)` + `@MessageHandler()`
- jangan implement bootstrap/drain sendiri jika memakai registry
- simpan `consumerOpts()` chain dan manual `ack/nak` di file consumer
- `*.service.ts` untuk use-case utama context (jika logic tidak trivial)
- `*.spec.ts` colocated
- gunakan `contracts/` untuk payload boundary
- gunakan `services/checker/support/` untuk reusable pendukung checker
- gunakan `utils/` hanya untuk pure function
Jangan buat nested folder `adapters/` atau `application/` di dalam context kecuali ada alasan kuat dan disetujui.