Skip to main content

Cross-Workflow Structure Rules (Prompt-Ready)

Dokumen ini adalah standar struktur folder dan tanggung jawab file untuk apps/worker-workflow/src/cross-workflow.

Tujuan:

  • menjaga struktur tetap konsisten dan mudah diikuti tim
  • memudahkan refactor tanpa mengubah behavior runtime
  • menjadi prompt/aturan kerja saat mengembangkan feature cross-workflow

1. Prinsip Utama

Gunakan pola:

  • context-first (feature module)
  • flat (hindari nested layer berlebihan jika belum perlu)
  • nama file menjelaskan peran

Konsep yang dipakai:

  • consumer = adapter transport (JetStream/NATS, ack/nak)
  • service = use-case utama cross-workflow
  • contracts = boundary agreement (type, interface, DTO)
  • utils = pure function/helper tanpa DI
  • custom decorator JetStream:
    • @JetStreamConsumer(...) = metadata subscribe (class-level)
    • @MessageHandler() = penanda method handler message (method-level)

2. Struktur Folder Standar

apps/worker-workflow/src/cross-workflow/
├── cross-workflow.module.ts
├── constants/
├── contracts/
├── docs/
├── services/
└── utils/

Aturan:

  • services/ cukup flat selama hanya ada 1 consumer + 1 service utama
  • contracts/ tetap di root (bukan di services)
  • utils/ tetap di root untuk pure helper
  • docs/ berisi behavior dan aturan (bukan runtime source)

3. Tanggung Jawab Tiap Folder

3.1 constants/

  • konstanta subject, consumer group, durable, deliver
  • tidak berisi logic runtime

3.2 contracts/

  • payload/contract boundary antar komponen
  • boleh berisi type/interface/DTO/abstract contract
  • tidak boleh berisi query DB / business logic / @Injectable()

Contoh saat ini:

  • cross-workflow.contract.ts

3.3 services/

  • *.consumer.ts untuk adapter transport
  • *.service.ts untuk use-case utama
  • test colocated (*.spec.ts)

3.4 utils/

  • helper pure function (contoh normalisasi relation type)
  • tidak mengakses DB/NATS

3.5 docs/

  • system behavior, struktur aturan, catatan pengembangan

4. Aturan Naming File

  • *.consumer.ts

    • adapter transport
    • contoh: cross-workflow.consumer.ts
  • *.service.ts

    • use-case utama / orchestration bisnis
    • contoh: cross-workflow.service.ts
  • *.contract.ts

    • boundary contract (type/interface/DTO)
    • contoh: cross-workflow.contract.ts
  • *.spec.ts

    • test colocated dengan runtime file
  • *.util.ts

    • pure utility function
    • contoh: relation-type.util.ts

5. Pembagian Tanggung Jawab File (Penting)

5.1 consumer

Boleh:

  • deklarasi metadata subscribe via @JetStreamConsumer(...)
  • decode CDC payload
  • filter event minimum (table/op/id)
  • mapping CDC -> trigger payload
  • manual ack/nak
  • memanggil CrossWorkflowService.execute(...)

Tidak boleh:

  • query DB kompleks
  • transaksi bisnis lintas tabel
  • logic readiness/target resolution

Catatan pola current:

  • bootstrap/registrasi consumer dijalankan JetStreamConsumerRegistryService
  • waitUntilReady() dijalankan registry (berdasarkan metadata consumer)
  • consumer tetap pegang manual ack/nak di @MessageHandler()

5.2 service

Boleh:

  • transaction boundary (QueryRunner)
  • source/target submission query
  • resolve connection config
  • relation-type branching (ONE_TO_MANY, MANY_TO_ONE)
  • readiness check
  • trigger target via SubmissionServiceV2

Tidak boleh:

  • subscribe/ack/nak concern
  • decode raw CDC payload

5.3 utils

Boleh:

  • normalisasi nilai config (mis. relationType)

Tidak boleh:

  • akses DB
  • DI Nest

6. Aturan Penempatan Type (Hitam Putih)

  1. Boundary payload/event lintas file/module -> contracts/*.contract.ts
  2. Type internal yang hanya dipakai test -> simpan di *.spec.ts
  3. Hindari top-level type/interface di runtime file kecuali benar-benar trivial dan lokal
  4. Jika internal type reusable mulai banyak, buat contracts/internal/*.types.ts

Contoh:

  • CrossWorkflowTriggerPayload -> contracts/cross-workflow.contract.ts

7. Pola Custom Decorator Untuk Consumer (Standar Saat Ini)

Gunakan pola ini untuk consumer JetStream:

  1. Class consumer diberi @JetStreamConsumer({...})
  2. Method handler message diberi @MessageHandler()
  3. consumerOpts() chain tetap ditulis di file consumer (readable)
  4. Manual ack/nak tetap ditulis di method handler
  5. Jangan implement OnApplicationBootstrap / OnModuleDestroy jika memakai registry

7.1 Contoh ringkas

@JetStreamConsumer({
name: 'Cross Workflow Trigger',
subject: String(CROSS_WORKFLOW_CONSUMER.SUBJECT),
waitUntilReady: true,
unhandledErrorNakDelayMs: 8000,
buildOpts: () =>
consumerOpts()
.durable(CROSS_WORKFLOW_CONSUMER.DURABLE)
.deliverGroup(CROSS_WORKFLOW_CONSUMER.GROUP)
.deliverTo(CROSS_WORKFLOW_CONSUMER.DELIVER)
.manualAck()
.ackExplicit()
.deliverNew()
.maxDeliver(CROSS_WORKFLOW_CONSUMER.MAX_REDELIVERY),
})
@Injectable()
export class CrossWorkflowConsumer {
constructor(private readonly service: CrossWorkflowService) {}

@MessageHandler()
async handle(msg: JetStreamConsumableMessage) {
// decode -> filter -> map -> service.execute -> ack/nak
}
}

7.2 Registry yang wajib tersedia di module

  • import DiscoveryModule (@nestjs/core)
  • register JetStreamConsumerRegistryService sebagai provider module

8. Aturan Refactor (Safety Rules)

Saat refactor:

  • utamakan behavior-preserving
  • ubah struktur/naming dulu, jangan ubah behavior bisnis sekaligus
  • setelah perubahan:
    • update import path
    • jalankan typecheck worker-workflow
    • jalankan test cross-workflow

Checklist minimum:

  • pnpm exec tsc -p apps/worker-workflow/tsconfig.json --noEmit
  • pnpm exec jest apps/worker-workflow/src/cross-workflow/services/cross-workflow.consumer.spec.ts apps/worker-workflow/src/cross-workflow/services/cross-workflow.service.spec.ts packages/lib/src/service/nats/jetstream-consumer/jetstream-consumer-registry.service.spec.ts --runInBand

9. Prompt Refactor (Siap Pakai)

Refactor folder/file di `apps/worker-workflow/src/cross-workflow` mengikuti aturan:
- context-first
- struktur flat (`constants`, `contracts`, `services`, `utils`, `docs`)
- transport adapter memakai suffix `*.consumer.ts`
- use-case utama memakai suffix `*.service.ts`
- boundary payload/type berada di `contracts/*.contract.ts`
- util pure function berada di `utils/*.util.ts`
- jika consumer memakai JetStream:
- gunakan `@JetStreamConsumer(...)` pada class
- gunakan `@MessageHandler()` pada method handler
- simpan `consumerOpts()` chain dan manual `ack/nak` di file consumer
- jangan ubah behavior runtime kecuali diminta

Setelah refactor:
1. update import path
2. jalankan typecheck worker-workflow
3. jalankan test `cross-workflow`
4. laporkan file yang dipindah/diubah dan risiko perubahan

10. Prompt Pengembangan Behavior Baru (Siap Pakai)

Tambahkan behavior baru di feature `apps/worker-workflow/src/cross-workflow` dengan aturan:
- filter CDC dan decode tetap di `cross-workflow.consumer.ts`
- transaksi, query, dan orchestration bisnis di `cross-workflow.service.ts`
- payload boundary baru ditaruh di `contracts/*.contract.ts`
- helper pure function ditaruh di `utils/*.util.ts`
- pertahankan manual `ack/nak` di consumer
- pertahankan behavior-preserving untuk flow lama

Setelah implementasi:
1. update/ tambah test consumer dan service
2. jalankan typecheck worker-workflow
3. laporkan perubahan behavior (jika ada)