STUDI KASUS
Navigasi dokumen: gunakan 03.overview untuk boundary dan ownership arsitektur, 02.roadmap untuk status/urutan implementasi, dan
AGENTS.mdpada root repository sebagai aturan eksekusi serta pembaruan dokumen.
Misalnya ada bisnis:
Organization:
Satoe Coffee Indonesiamemiliki:
- 3 cabang
- 25 karyawan
- payroll bulanan
- attendance GPS
- inventory
- shift
- HR manager
- supervisor
- kasir
STEP 1 ā USER REGISTER
User membuat akun
User:
- email
- password
- oauth
- MFADatabase
User
āāā id
āāā email
āāā password
āāā platformRolePenting
User belum punya:
- salary
- attendance
- payroll
- jabatan
Karena:
User hanya identity.
Readiness identity
Pada tahap ini sistem hanya boleh menampilkan UserProfileReadiness: kelengkapan profil personal yang tidak membutuhkan Organization, Member, Employee, Team, atau Workspace. Jika Ahmad kemudian menjadi owner Satoe Coffee dan Sarah menjadi Member/Employee, masing-masing memperoleh projection OrganizationReadiness dan MemberWorkReadiness yang terpisah. Dengan demikian pegawai Pemkab pada studi kasus 17 Agustus tidak pernah memperoleh atau dipengaruhi readiness Organization A hanya karena ia menjadi participant collaboration terbatas.
STEP 2 ā USER MEMBUAT ORGANIZATION
Ahmad membuat Organization
Organization:
Satoe Coffee IndonesiaDatabase
Organization
āāā id
āāā name
āāā ownerId
āāā etcRelasi
User (Ahmad)
āāā owns Organization (ownedOrganizations)STEP 3 ā OWNER MENJADI MEMBER
Ketika Organization dibuat:
sistem otomatis membuat:
MemberKenapa?
Karena:
Hanya member organisasi yang bisa mengakses organisasi tersebut, owner juga perlu akses ke Organization.
Database
Member
āāā userId
āāā organizationId
āāā permissionRoleId
āāā statusSTEP 4 ā ORGANIZATION MEMBUAT PERMISSION ROLE
Ahmad membuat role:
owner
hr_manager
supervisor
cashier
warehouse_adminDatabase
PermissionRole
āāā name
āāā permissions[]
āāā organizationIdContoh
cashier
punya permission:
order.create
order.view
attendance.checkinhr_manager
punya permission:
employee.create
payroll.generate
payroll.approve
attendance.approveSTEP 5 ā ORGANIZATION MEMBUAT POSITION
Ahmad membuat struktur jabatan
CEO
Branch Manager
Supervisor
Barista
Cashier
Kitchen StaffDatabase
Position
āāā name
āāā parentId
āāā organizationIdHierarchy
CEO
āāā Branch Manager
āāā Supervisor
āāā Cashier
āāā BaristaKenapa Position dipisah dari Permission Role?
Karena:
Position:
Cashier
Permission Role:
pos_operatoratau:
Position:
Branch Manager
Permission Role:
limited_managerSTEP 6 ā OWNER MENGUNDANG USER
Ahmad mengundang Sarah
email:
sarah@gmail.comDatabase
Invitation
āāā email
āāā token
āāā target="member" ("user"|"member"|"employee")
āāā inviterId
āāā inviteeId
āāā organizationId (ketika undangan member untuk workspace menggunakan workspaceId)
Member
āāā userId
āāā organizationId
āāā permissionRoleId
āāā etcSarah menerima undangan
Sekarang:
Sarah
āāā menjadi Member (Organization)STEP 7 ā SARAH MENJADI EMPLOYEE
Penting
Tidak semua member adalah employee.
Tetapi Sarah adalah staff.
Maka dibuat:
EmployeeDatabase
Employee
āāā userId
āāā organizationIdId
āāā memberId
āāā employeeCode
āāā positionId
āāā type
āāā status
āāā etcKenapa Employee dipisah?
Karena sekarang Sarah punya:
- payroll
- BPJS
- kontrak
- attendance
- shift
- salary
STEP 8 ā POSITION HISTORY
Sarah awalnya Cashier
CashierDatabase
EmployeePositionHistory
āāā employeeId
āāā newPositionId
āāā effectiveFrom
āāā changedById1 tahun kemudian
Sarah dipromosikan:
SupervisorHistory baru dibuat
Cashier -> SupervisorKenapa ini penting?
Karena payroll lama:
- harus tetap tahu Sarah dulu cashier
- attendance approval lama tetap valid
- laporan historis tetap akurat
STEP 9 ā ROLE HISTORY
Awalnya permission Sarah:
cashier_roleSetelah promosi
menjadi:
supervisor_roleDatabase
RoleHistory
āāā oldRoleId
āāā newRoleId
āāā changedById
āāā effectiveAtKenapa PositionHistory dipisah dari RoleHistory?
Karena:
Position:
Cashier -> Supervisor
tetapi
Permission:
pos_operator -> limited_managerdan itu domain berbeda.
MEMBUAT WORKSPACE
Dalam keputusan internal Owner menunjuk sarah sebagai kepala tim dalam agenda tertentu.
Sarah membuat:
Workspace
ā Mengundang anggota beberapa lainnya.
ā etcSehingga setiap anggota workspace sudah pasti Member Organization, namun tidak semua member Organization adalah anggota workspace tersebut.
STEP 10 ā ATTENDANCE
Sarah check-in
mobile app mengirim:
clockIn
GPS
selfie
device
IPDatabase
Attendance
āāā employeeId
āāā clockIn*
āāā clockOut*
āāā clockInLocation
āāā lateMinutes
āāā overtimeMinutes
āāā approvedByIdGeoPoint
GeoPoint
āāā latitude
āāā longitude
āāā accuracy
āāā deviceKenapa GeoPoint penting?
Karena:
- anti fake GPS
- geofence
- audit lokasi
- field tracking
STEP 11 ā PAYROLL CONFIGURATION
Sarah punya:
basic salary
meal allowance
BPJS
tax
overtimeDatabase
EmployeeCompensationComponent
āāā componentType
āāā category
āāā amount
āāā rateContoh
BASIC_SALARY
5.000.000BPJS_HEALTH
1%STEP 12 ā PAYROLL GENERATION
Sistem generate payroll bulan Mei
Payroll dibuat
Payroll
āāā grossSalary
āāā totalDeduction
āāā netSalary
āāā status
āāā periodPayrollItem dibuat
Basic Salary
Overtime
Meal Allowance
BPJS
Tax
Late DeductionKenapa Payroll immutable?
Karena:
payroll bulan lalu tidak boleh berubah.
Walaupun:
- salary sekarang berubah
- position berubah
- BPJS berubah
STEP 13 ā INVENTORY DAN COMMERCE LINTAS DATABASE
STEP 13 bukan transaksi dalam satu database global. Organization adalah tenant root di Runner; catalog, commerce, dan inventory menyimpan organizationId sebagai tenant key terverifikasi oleh API, bukan foreign key lintas database.
Runner DB: Organization, Member, Team, Workspace, Collaboration, Employee, PermissionRole
Billing DB: BillingAccount, Plan, Subscription, Entitlement, UsageRecord
Catalog DB: Product, Variant, Price, Category
Commerce DB: Customer, Order, OrderItem, Payment, Shipment
Inventory DB: Warehouse, Stock, InventoryMovement, ReservationSebuah workspace boleh dipakai sebagai konteks operasional pada dokumen domain bila modul membutuhkannya, tetapi tidak menggantikan organizationId. Semua query selalu di-scope oleh organization terlebih dahulu.
13A ā Penerimaan barang
Gudang menerima 50 kg kopi. Inventory API memvalidasi actor dari session, memastikan actor adalah member aktif tenant, lalu membuat ledger movement yang idempoten memakai reference/event key.
InventoryMovement
āāā organizationId
āāā warehouseId
āāā productId / variantId (referensi Catalog)
āāā type = IN
āāā quantity = 50
āāā referenceType = GOODS_RECEIPT
āāā referenceId
āāā idempotencyKeyWarehouseStock adalah proyeksi/materialized balance yang diperbarui bersama movement di Inventory DB. Ia bukan sumber audit; sumber kebenarannya tetap ledger movement.
13B ā Penjualan dan pengurangan stok
Order dibuat di Commerce DB setelah Commerce API memvalidasi tenant dan harga/snapshot dari Catalog. Ketika order mencapai state yang mengonsumsi persediaan, Commerce menerbitkan event/outbox; Inventory memproses event itu secara idempoten dan membuat OUT atau RESERVE movement. Tidak ada transaksi Prisma lintas Runner/Catalog/Commerce/Inventory.
Kegagalan antar layanan ditangani dengan outbox, retry terukur, dan rekonsiliasi; bukan dengan mengubah saldo stok dari UI atau menganggap request browser sebagai sumber kebenaran.
STEP 14 ā EMPLOYEE RESIGN, AKSES, DAN WORKSPACE
Ketika Sarah resign, tiga lifecycle dievaluasi secara terpisah:
User tetap ada (identity dan audit)
Member dapat tetap aktif, disuspensi, atau direvoke sesuai keputusan akses
WorkspaceParticipant direvoke hanya untuk workspace yang tidak lagi diikuti
Employee employmentStatus = RESIGNEDResign tidak otomatis menghapus Member. Seorang mantan karyawan mungkin tetap memiliki akses non-employment yang sah, misalnya sebagai owner, auditor, atau collaborator. Sebaliknya, revoke Member harus mencabut seluruh assignment WorkspaceParticipant aktif dan seluruh permission organisasi; Employee dan sejarah payroll tetap dipertahankan.
14A ā Konsistensi domain
- Runner DB menyimpan perubahan Member, WorkspaceParticipant, Employee, PositionHistory, dan RoleHistory dalam boundary domain Runner.
- Payroll yang sudah finalized tidak diubah. Koreksi dilakukan melalui adjustment/reversal baru sesuai domain payroll.
- Commerce dan Inventory tidak menghapus order atau movement historis ketika employee/member direvoke; referensi actor disimpan sebagai audit snapshot/ID yang aman.
14B ā Rehire
Saat Sarah kembali, sistem tidak membuat ulang User. HR mengaktifkan atau membuat employment record sesuai kebijakan, mencatat posisi efektif baru, lalu owner/authorized admin memberikan Member role dan assignment workspace yang diperlukan. Riwayat lama tetap immutable.
STEP 15 ā CATEGORY SEBAGAI KONFIGURASI, BUKAN OTORISASI
Organization.category membatasi vocabulary, template, dan urutan modul yang relevan (misalnya retail menonjolkan catalog/commerce/inventory; service menonjolkan booking). Category bukan role, permission, workspace, atau entitlement. Ia tidak memberi akses dan tidak boleh menjadi satu-satunya dasar API authorization.
Host container hanya boleh menampilkan shortcut category apabila route/micro-frontend telah benar-benar terpasang dan permission API telah tersedia. Category tidak boleh menghasilkan URL palsu untuk product, order, inventory, atau analytics.
STEP 16 ā SUBSCRIPTION, ENTITLEMENT, DAN RETENSI
Billing adalah control-plane terpisah, bukan bagian dari Runner ataupun Commerce.
Satu BillingAccount awalnya merepresentasikan satu Organization melalui
organizationId scalar yang diverifikasi ke Runner. Invoice Billing adalah
tagihan Aori kepada Organization; ia bukan Invoice Commerce antara Organization
dan customer.
Billing DB
āāā BillingAccount identitas penagihan tenant
āāā Plan versi produk/komersial Aori
āāā Subscription lifecycle subscription dan snapshot plan
āāā Entitlement feature/quota efektif tenant
āāā UsageRecord event metering immutable dan idempoten
āāā BillingInvoice tagihan Aori ā Organization
āāā BillingPayment pembayaran tagihan AoriSetiap mutasi berfitur memeriksa dua boundary secara terpisah:
1. Entitlement: tenant berhak memakai feature dan belum melewati limit?
2. Authorization: actor memiliki permission untuk melakukan aksi?Satu boundary tidak menggantikan yang lain. Plan tidak memberi user hak untuk
mengundang partner, dan permission tidak dapat melampaui quota plan. Domain yang
memiliki resource menghitung usage secara atomik dan kemudian mengirim
UsageRecord dengan idempotency key; Billing tidak boleh menjadi dependency write
sinkron untuk seluruh domain operasional.
Ketika subscription berhenti, transisi minimalnya adalah active ā
grace_period ā restricted_read_only (policy application) ā archived ā
purged. Data tidak langsung dihapus: data aktif dipertahankan selama periode
pembukuan, kemudian diarsipkan, dan hanya dipurge jika retention policy/legal
terpenuhi setelah pemberitahuan serta kesempatan export backup kepada Organization.
STEP 17 ā KOLABORASI ANTAR-ORGANIZATION YANG DAPAT DIAUDIT
Misal: User X membuat Organisasi A dan melengkapi seluruh entity lainnya (seperti: permissionRole, dll), User X mengundang Member baru sebagai HR, HR otomatis menjadi Member dan karyawan (karena harus ada payroll), HR merekrut karyawan baru, HR mengelompokkan beberapa karyawan dalam satu Team misal Team bernama T1. Team T1 memiliki beberapa anggota (semua anggota Team sudah pasti dan harus merupakan Member Organization dan Employee), HR menugaskan beberapa Member/Employee untuk membuat sebuah agenda acara ulang tahun perusahaan (dapat lintas Team), Member/Employee yang ditugaskan tersebut akan di kumpulkan didalam satu workspace (contoh kasus workspace internal Organisasi). Kemudian suatu saat ada agenda 17 Agustusan, HR menugaskan beberapa Member/Employee untuk megurus agenda tersebut, maka akan dikumpulkan lagi didalam satu workspace baru yang berbeda, ternyata acara 17 Agustusan diadakan di halaman pemkab, maka organisasi A harus menerbitkan surat tugas dan surat kerjasama, untuk prosesi pembahasan yang transparan maka pegawai pemkab akan diundang untuk masuk kedalam workspace baru tersebut.
Kasus acara 17 Agustus menunjukkan bahwa workspace bukan organisasi virtual dan participant eksternal tidak boleh dipindahkan ke tenant host. Organization A
memiliki workspace Panitia 17 Agustus; Pemkab tetap organization mandiri. Pegawai Pemkab menjadi Member Pemkab, lalu diberi scope kolaborasi terbatas.
Organization A (owner sah Workspace)
āāā Workspace: Panitia 17 Agustus
āāā participant internal: Member/Employee A dari beberapa Team
āāā participant eksternal: Member Pemkab, scope terbatas
Collaboration: Panitia 17 Agustus
āāā Party: Organization A role=owner
āāā Party: Pemkab role=participant
āāā Party: Sponsor B role=participant
āāā Party: Team HR A role=approver/internal participantProject menyimpan target, timeline, budget, deliverable, dan status.
Collaboration menyimpan legal basis, party, approval, scope, masa berlaku, dan
dokumen. Workspace adalah ruang operasional agenda. Ketiganya bukan sinonim:
satu Project dapat memiliki banyak Collaboration/Workspace dan satu
Collaboration dapat mengatur banyak Workspace milik organization owner.
CollaborationParty
āāā organizationId wajib; teamId opsional
āāā role: owner | participant | observer | approver
āāā status: invited | accepted | suspended | revoked
āāā grantedScopes dan masa berlaku
āāā representedByMemberId / assignmentDocumentId bila relevan
CollaborationApproval
āāā requiredByRole / requiredByTeamRole
āāā party yang harus menyetujui
āāā approvedByMemberId, status, decidedAt, note
CollaborationDocument
āāā agreement | assignment_letter | nda | minutes | attachment
āāā issuerOrganizationId, document reference opaque, validity, verificationUrutan aman participant eksternal
- Member internal berpermission membuat draft Collaboration dan party owner.
- API memverifikasi member aktif, organization owner, serta Team bila ada.
- Party eksternal dibuat
invited; approval wajib dibuat menurut policy. - Partner menerima dengan Member aktif dari organization party yang sama.
- Workspace hanya dihubungkan setelah approval wajib selesai dan semua validity window aktif.
- External
WorkspaceParticipantwajib menunjuk party aktif; scope-nya subset party dan tidak boleh melampaui masa berlaku party/collaboration. - Suspend, revoke, expiry, atau termination harus segera menolak akses baru; histori dan dokumen tetap auditable.
STEP 18 ā INVARIANT IMPLEMENTASI
| Area | Invariant yang harus divalidasi API |
|---|---|
| Organization | Tenant root; workspace tidak pernah menjadi tenant key. |
| Workspace | Owner tepat satu organization; collaboration link hanya boleh dari owner organization yang sama. |
| Team | Team.organizationId wajib; TeamMember.memberId wajib dari tenant sama; employee opsional juga dari tenant sama. |
| Member | Access lifecycle terpisah dari Employee. Revoke/suspend berbasis permission dan audit trail. |
| External participant | Member aktif tenant asal; tidak pernah menciptakan Member tenant host. |
| Collaboration | Minimal dua party saat activation; owner party merepresentasikan ownerOrganizationId; granted scope tidak melampaui scope collaboration. |
| Approval | Actor approver memenuhi role/team role yang disyaratkan dan berasal dari party relevan. |
| Billing | Entitlement menentukan capability/quota tenant; permission menentukan otoritas actor. Keduanya wajib. |
| Money | BigInt adalah minor unit currency; jangan menerima float/total yang dihitung client. |
| Cross database | Scalar ID tervalidasi, outbox/inbox, idempotency key, projection, reconciliation; tidak ada FK/join/transaksi Prisma lintas database. |
Retensi
active ā archived setelah periode pembukuan ā evaluasi retention/legal hold
ā peringatan dan export backup ā purged oleh job yang scoped serta auditable
jika policy mengizinkan. Archive bukan delete; payroll finalized, ledger stock,
invoice, approval, dan audit evidence tidak boleh dihapus hanya karena akses
Member dicabut.
Kesimpulan batas domain
Organization tenant root dan sumber tenant-scoping
Workspace unit kerja opsional di bawah Organization
Member akses organisasi dan RBAC
WorkspaceParticipant assignment banyak-ke-banyak ke unit kerja; participant eksternal tetap member tenant asalnya
Employee lifecycle ketenagakerjaan
Category konfigurasi pengalaman/modul bisnis, bukan otorisasiKesimpulan paling penting
Sekarang coba lihat:
USER
hanya:
identity
authenticationMEMBER
hanya:
Organization access
RBAC
permissionsEMPLOYEE
hanya:
employment
payroll
attendance
HRPOSITION
hanya:
organizational hierarchyPERMISSION ROLE
hanya:
access controlPAYROLL
hanya:
immutable salary snapshotINVENTORY MOVEMENT
hanya:
stock ledgerDan inilah inti dari enterprise architecture
Bukan:
membuat banyak tabletetapi:
memisahkan lifecycle dan responsibility tiap domainHealthy Architecture
Database Runner Schema
User
āāā identitas/autentikasi layer
āāā dapat memiliki banyak Organization
āāā dapat tergabung di banyak Organization
Organization
āāā memiliki banyak Member
āāā dapat memiliki banyak Team
āāā dapat memiliki banyak Workspace
āāā dapat memiliki banyak Project
āāā menjadi owner Collaboration yang dibuat dalam tenant-nya
Member // "siapa yang boleh mengakses Organisasi ini?"
āāā kontrol akses
āāā milik Pengguna
āāā milik Organization
āāā memiliki permission role
Employee // "siapa yang bekerja untuk organisasi ini?"
āāā berasal dari Member (member bisa jadi employee, tapi employee harus member)
āāā tenaga kerja / SDM (profil)
Team // "kumpulan member dan/atau employee"
āāā organizationId wajib; tidak pernah lintas tenant
āāā TeamMember.memberId wajib dan employeeId opsional
āāā dapat dikaitkan ke Workspace, tetapi bukan milik Workspace
āāā kumpulan tenaga kerja / SDM internal yang stabil
Project // "objek kerja dan deliverable"
āāā target, timeline, budget, dan hasil
āāā dapat memiliki beberapa Collaboration dan Workspace
Collaboration // "legal basis, parties, approval, dan scope bekerja bersama"
āāā dapat bertipe internal_team, inter_organization, external_partner, event_committee, atau project_delivery
āāā memiliki dua atau lebih CollaborationParty; party organisasi/team menyimpan role, status, scope, dan masa berlaku
āāā menyimpan approval dan dokumen legal secara auditable
āāā dapat mengatur beberapa Workspace milik organization owner
Workspace // "ruang kerja operasional untuk agenda"
āāā organizationId wajib; hanya satu Organization yang menjadi owner sah
āāā mengumpulkan participant internal lintas Team untuk sebuah agenda
āāā participant eksternal berasal dari Organization asalnya melalui CollaborationParty aktif dan scope terbatas
āāā tidak menjadikan participant eksternal sebagai Member Organization ownerMisalnya:
-
Pemilik Organisasi:
- Bisa login
- Bisa melihat dashboard
- Bisa melihat revenue
- Tetapi:
- Bukan employee
- Tidak menerima payroll
- Tidak punya absensi
- Maka:
- Member = ada
- Employee = tidak ada
- Pemilik Organisasi bisa jadi member / karyawan di organisasi lain
- Maka:
- Member = ada (dalam organisasi lain tersebut)
- Employee = ada (dalam organisasi lain tersebut)
-
Auditor eksternal:
- boleh melihat laporan
- bukan karyawan
- Maka:
- Member = ada
- Employee = tidak ada
-
Karyawan resign:
- Kita TIDAK BOLEH menghapus:
- payroll[1]
- attendance[1]
- histori kerja[1]
- Akses login dan Member dievaluasi lalu dicabut berdasarkan permission serta kebijakan HR, bukan melalui jalur operasional developer.
- Maka:
- Member[2] = nonaktif / revoked hak akses sesuai permission dan audit trail
- Employee[2] = tetap ada agar hubungan kerja serta histori pembukuan kredibel
- User[2] = tetap ada sebagai identitas digital semi-permanen
- Kita TIDAK BOLEH menghapus:
[1] Data aktif tersedia selama periode pembukuan. Setelahnya sistem mengarsipkan data; purge permanen hanya setelah retention policy/legal terpenuhi dan organization telah diperingatkan serta diberi kesempatan mengunduh backup. [2] Perubahan akses tidak menghapus history secara langsung. Retensi dan purge mengikuti kebijakan yang sama, legal hold, dan audit trail.
ideal:
- auth lifecycle terpisah
- HR lifecycle terpisah
- aman
- fleksibel
- enterprise-ready
scalable:
- login lifecycle ā employment lifecycle
- access control ā HR lifecycle
- employee resign ā account deleted
fokus User:
- login
- session
- auth
- credential
- MFA
- OAuth