BMP
Business Management Platform
Project Blueprint & Design Documentation
Dokumen Gabungan — v1.0
Dua “Architecture” yang Berbeda Level 4
0. Latar Belakang — Analisis 12 ERP Open Source 5
Chapter 1: Design Foundation 6
Chapter 2: Interaction Structure 11
2.3 Navigation & Information Structure 13
Global Navigation vs Local Navigation 13
Lima Hal Spesifik Navigasi ERP/BMP 14
Conditional Field vs Progressive Disclosure 18
2.5 Search & Command Palette 18
2.6 Notification & Approval Workflow 20
Pola Fork-Join Checker Lintas Divisi 20
2.7 Personalization & Account Model 22
Model Akun, bukan Persona Gabungan 22
2.9 Responsive & Usage Context 25
Catatan: Konvensi Bahasa UI Sistem 26
1. Fondasi: Phoenix — Ash — Event-driven 27
3. Data Layer — Full SQLite 28
4. Audit, Retensi & Kepatuhan (final) 28
7. Observability, I18n, Storage 29
8. Testing & Static Analysis 29
10. Hierarki Pemilihan Dependency (kebijakan resmi) 30
Addendum: Mekanisme Sync-Agent — Penjelasan Detail 30
Chapter 4: Development Conventions 35
Chapter 6: Report & Workflow 37
Chapter 8: Modul Optional/Community 39
Dokumen ini adalah blueprint proyek BMP (Business Management Platform) secara utuh — menggabungkan seluruh keputusan desain dan arsitektur yang telah disepakati ke dalam satu struktur dokumentasi resmi, menggantikan catatan sesi yang sebelumnya tersebar di beberapa file terpisah.
Seluruh cakupan proyek dipetakan ke dalam empat kelompok besar berikut, masing-masing dengan status pengerjaan saat dokumen ini disusun:
| Cluster | Item | Status |
| UI | Design Foundation (Theme, Color System, Typography, Iconography) | Selesai — Chapter 1 |
| UI | Interaction Structure (Layout, Nav, Dashboard, Components, Workflow) | Selesai — Chapter 2 |
| UI | System UI/UX (Module toggle, Permission, Role/Account, Workspace, Settings) | Belum — Chapter 5 |
| Architecture | Phoenix, Ash, event-driven, sync-agent, deployment profile | Selesai — Chapter 3 |
| Development | Coding Standard, Naming, Folder Convention, Generator, Testing, CI/CD, Extension SDK, Migration, Versioning | Belum — Chapter 4 |
| Modul ERP | Administration, System & Utilities, Accounting, Inventory, Purchase, Sales, Manufacturing, HR, Asset Mgmt, Quality, Project Mgmt (Core) | Belum — Chapter 7 |
| Modul ERP | POS, E-commerce, Website, Helpdesk (Official); Fleet, Rental, Healthcare, Agriculture, Hospitality, dll. (Community, togglable) | Belum — Chapter 8 |
| Umum | Report & Workflow lintas modul | Belum — Chapter 6 |
Penting dipisahkan agar tidak rancu: “System UI/UX” (Chapter 5) dan “Architecture” (Chapter 3) sama-sama menyentuh topik Module/Permission/Role/Workspace, tapi dari sudut pandang berbeda.
| Chapter | Sudut pandang | Contoh |
| 5. System UI/UX | Bagaimana konsep ini terlihat & terasa oleh user/admin | Halaman kelola Role, toggle modul di UI, tampilan Workspace switcher |
| 3. Architecture | Bagaimana itu benar-benar berjalan di level infrastruktur | Ash.Policy.Authorizer menegakkan Permission, Reactor menjalankan Workflow |
Urutan bab dalam dokumen ini disusun berdasarkan ketergantungan — apa yang harus mapan dulu sebelum yang lain bisa dibangun konsisten di atasnya, bukan sekadar urutan sembarang:
Sebelum menyusun prinsip desain BMP, dilakukan analisis terhadap 12 aplikasi ERP open source sebagai pembanding: ERPNext, Dolibarr, Apache OFBiz, metasfresh, iDempiere, Tryton, OpenPetra, Axelor, inoERP, LedgerSMB, BlueSeer, dan WebERP.
Kesimpulan utama: hampir semua ERP tersebut membangun UI dengan mengikuti struktur backend (skema database, metadata resource), bukan mengikuti alur kerja/pengalaman user. Pola kesalahan yang berulang:
Prinsip inti yang menjadi fondasi seluruh dokumen ini:
UI yang nyaman selalu menjawab “apa yang sedang ingin dicapai user saat ini” — bukan “bagaimana data ini disimpan di backend”.
BMP (Business Management Platform) secara sadar dirancang untuk UMKM yang sedang berkembang menuju skala menengah — bukan enterprise. Konsekuensinya: default harus cukup sederhana untuk dipakai tanpa training/konsultan, approval bertingkat bersifat opsional (bukan default wajib), dan satu orang boleh memegang lebih dari satu akun fungsi sesuai kebutuhan bisnis kecil yang masih hemat sumber daya.
Design Foundation adalah bahasa visual dasar yang dipakai seluruh komponen UI (button, badge, toast, card). Bagian ini didahulukan sebelum Interaction Structure karena semua komponen bersama yang dirancang di Bab 2 bergantung pada token yang ditetapkan di sini — makin lama ditunda, makin banyak yang harus diubah retroaktif.
Arah visual BMP: profesional tapi tidak mengintimidasi — berbeda dari kesan “software korporat kaku” ala ERP enterprise yang membuat pemilik UMKM merasa perlu training dulu sebelum berani memakainya.
Token warna bersifat semantik (berdasarkan fungsi), bukan sekadar warna brand. Satu token = satu makna, berlaku konsisten di seluruh komponen.
| Token | Fungsi | Nilai |
|---|---|---|
| --color-accent | Aksi utama, elemen aktif/terpilih | #0D9488 (teal) |
| --color-accent-bg | Latar lembut untuk elemen accent | #ECFDF9 |
| --color-success / --bg-success | Status positif (disetujui, berhasil) | #15803D / #F0FDF4 |
| --color-warning / --bg-warning | Butuh perhatian, belum kritis | #B45309 / #FFFBEB |
| --color-danger / --bg-danger | Kritis, destruktif, overdue | #B91C1C / #FEF2F2 |
| --text-primary | Teks utama | #18181B |
| --text-secondary | Teks pendukung | #52525B |
| --text-muted | Teks tersier / placeholder | #8B8B93 |
| --border | Garis pemisah standar | #E4E4E7 |
| --surface-1 / --surface-2 | Latar sekunder (card) / latar utama | #F7F7F8 / #FFFFFF |
Teal dipilih sebagai warna accent (bukan biru/ungu seperti kebanyakan SaaS) supaya BMP memiliki identitas visual sendiri yang tidak generik, dengan asosiasi tenang dan terpercaya tanpa terasa dingin seperti biru korporat murni.
Dua kebutuhan khusus aplikasi bertipe ERP/BMP yang sering diabaikan desain UI generik:
Font terpilih: Inter — open source, mendukung tabular numerals bawaan, teruji untuk UI aplikasi data-berat.
Karena aplikasi berjalan offline/LAN-only, font wajib di-bundle lokal (file .woff2 di priv/static/fonts/), tidak boleh bergantung pada Google Fonts CDN.
| Skala | Ukuran | Pemakaian |
|---|---|---|
| text-xs | 11px | Caption, label kolom tabel |
| text-sm | 13px | Body dasar (list, form) |
| text-base | 14px | Teks utama form / paragraf |
| text-lg | 16px | Judul dokumen / card |
| text-xl | 20px | Judul halaman / section |
Berat font dibatasi 2 saja: Regular (400) untuk body, Medium (500) untuk judul/emphasis — mencegah developer bebas memakai Bold/Semibold/Black secara tidak konsisten.
Satu sumber ikon untuk seluruh aplikasi, bukan campuran beberapa icon set.
— widget: "design_foundation_styleguide"_
Lembar acuan visual: swatch warna, contoh type scale dengan tabular numerals, dan sampel ikon dua ukuran._
Dua contoh berikut menunjukkan token di atas diterapkan pada tampilan halaman web yang mendekati bentuk final (bukan wireframe kotak-kotak).
— widget: "bmp_home_high_fidelity"_
Halaman Home dengan sidebar ikon, search bar, feed My Attention, dan 3 KPI card dengan tabular numerals._
— widget: "bmp_list_view_high_fidelity"_
Tabel Sales Order dengan tab saved-view, badge status berwarna, dan kolom nilai rata kanan tabular numerals._
Bagian ini mendefinisikan bagaimana tiap jenis layar disusun secara konsisten di seluruh modul BMP — mengikuti prinsip inti Bab 0: alur kerja user, bukan struktur backend.
Masalah pada ERP pembanding: landing page berupa “app launcher” (grid ikon besar) menyisakan ruang kosong besar saat modul sedikit; semua ikon modul diberi bobot visual sama padahal frekuensi pemakaian berbeda jauh; tidak menjawab “apa yang perlu dikerjakan hari ini”.
Prinsip pengorganisasian:
— widget: "erp_home_consolidated_revision"_
Hasil konsolidasi landing page: feed My Attention, KPI ringkas, dan dokumen terakhir — dibanding versi awal yang terlalu ramai._
Masalah pada ERP pembanding: kolom mengikuti urutan tabel database; status hanya teks polos tanpa sinyal visual; filter tersembunyi di panel terpisah; bulk action sulit ditemukan.
Prinsip pengorganisasian:
— widget: "bmp_list_view_high_fidelity"_
List view Sales Order versi high-fidelity — dapat dipakai ulang sebagai referensi visual bagian ini._
Koreksi konsep penting: navigasi bukan flowchart (satu jalur linear). Model yang tepat adalah peta kedalaman dengan panah dua arah di tiap level, ditambah jalur pintas (search) yang dapat melompati level tengah.
— widget: "navigasi_bukan_flowchart_linear"_
Diagram lima level kedalaman navigasi (Home → Workspace → List → Detail → Sub-record) dengan panah dua arah dan jalur pintas via search._
| Global Navigation | Local Navigation | |
|---|---|---|
| Definisi | Tidak berubah di manapun user berada | Berubah total tergantung level/konteks saat ini |
| Isi | App switcher, search universal, notifikasi, identitas akun, shortcut Home | Breadcrumb, tab dalam record, related-list/link |
| Aturan | Isi minim | Maksimal 5–6 tab; breadcrumb maksimal 3–4 level |
— widget: "global_vs_local_navigation"_
Kombinasi rak ikon (global nav) yang identik di semua halaman dengan breadcrumb + tab (local nav) yang berubah sesuai konteks._
Tree bersarang (expand/collapse permanen) tidak disarankan karena mencerminkan struktur database, bukan frekuensi pemakaian. Sidebar flat (1 level) adalah baseline yang benar; flyout saat hover dipakai untuk shortcut cepat tertentu tanpa mengubah bentuk sidebar utama.
— widget: "sidebar_flyout_vs_tree"_
Perbandingan sidebar flat dengan flyout munculan saat hover, tanpa mengubah tinggi sidebar utama._
— widget: "document_trail_navigation"_
Rantai dokumen Quotation → Sales Order → Delivery Note → Invoice → Payment dengan status kini disorot._
— widget: "settings_hub_discoverable"_
Settings Hub sebagai jalur discovery untuk config/master data, dikelompokkan per kategori fungsional._
widget: "erp_full_page_navigation_concept"_
Contoh satu halaman penuh yang menggabungkan global nav, local nav, status-aware action bar, dan document trail sekaligus._
Masalah pada ERP pembanding: field diurutkan sesuai skema tabel; tabel item/baris sulit dinavigasi keyboard; validasi baru muncul setelah submit; field read-only tidak dibedakan visual.
Prinsip pengorganisasian:
— widget: "form_detail_view_organized"_
Form Sales Order tersusun Who → What → When/How dengan validasi inline dan field read-only bertanda visual._
— widget: "form_advanced_section_expanded"_
Bagian Advanced Details dalam kondisi terbuka, termasuk contoh conditional field (metode pembayaran)._
| Progressive Disclosure | Conditional Field | |
|---|---|---|
| Siapa kontrol | User (klik untuk buka) | Sistem (otomatis berdasar nilai field lain) |
| Kapan muncul | Kapan saja user mau lihat | Hanya saat kondisi tertentu terpenuhi |
| Tujuan | Menyembunyikan yang jarang dipakai | Menyembunyikan yang tidak relevan di konteks itu |
Aturan desain conditional field: jangan bikin layout “lompat” kasar; validasi harus ikut kondisi aktif (field tersembunyi tidak boleh dianggap wajib); data lama tidak boleh hilang diam-diam saat kondisi berubah; jangan berantai lebih dari 2 tingkat.
Masalah pada ERP pembanding: search tercecer per-modul; hasil tidak dikelompokkan; tidak ada “command” (search hanya bisa navigasi); kotak kosong tidak menawarkan apa-apa.
Prinsip pengorganisasian: kelompokkan hasil berdasarkan jenis (Action → Document → Report → Setting); toleran typo & bisa cari lewat kode/nomor dokumen; prefix untuk mempersempit cepat (# report, > action); sajikan status di tiap hasil; kotak kosong menampilkan Recent Activity & Quick Actions sebelum user mengetik.
— widget: "command_palette_organized"_
Command palette dengan hasil terkelompok: Action, Document (dengan badge status), dan Report._
Masalah pada ERP pembanding: sebagian besar tidak punya sistem notifikasi terpadu; notifikasi terpecah ke banyak sumber; harus buka dokumen penuh untuk approve hal sederhana; tidak ada progres approval bertingkat yang terlihat; approval yang didiamkan lama diperlakukan sama seperti yang baru masuk.
Terminologi resmi alur approval BMP: Submitter → Checker (paralel, lintas divisi) → Approver (final).
— widget: "notification_approval_flow"_
Feed notifikasi terpadu (item actionable vs info-only) dan contoh progres approval bertingkat linear (stepper 2 tingkat)._
Untuk kasus seperti Purchase Order yang membutuhkan verifikasi dari lebih dari satu divisi (contoh: Manager Production & Manager Accounting) sebelum sampai ke Approver akhir (Direktur/GM), alur tidak lagi linear melainkan fork-join:
— widget: "fork_join_approval_flow"_
Diagram fork-join: Purchasing submit → dua Checker paralel (Manager Production, Manager Accounting) → Approver terkunci sampai kedua Checker selesai._
Catatan implementasi: pola ini secara alami dipetakan ke Reactor (fan-out/fan-in dengan compensation saat salah satu step gagal) — dibahas lebih lanjut di bagian Architecture.
Tiga lapisan yang dipisah tegas: Permission (apa yang boleh dilihat/dilakukan, diatur admin) → Role Default (dari yang boleh diakses, mana yang relevan ditampilkan duluan untuk fungsi tertentu, template per fungsi) → Personal Customization (penyesuaian individu di atas default).
— widget: "role_based_personalization_layers"_
Role default per fungsi (Sales/Warehouse/Finance) sebagai template admin, dengan contoh override personal seorang user dan opsi reset ke default._
Keputusan desain BMP: satu orang dapat memiliki lebih dari satu akun terpisah (contoh: budi.kasir dan budi.pemilik), bukan satu akun dengan banyak peran digabung otomatis. Personalisasi dan permission melekat ke akun, bukan ke individu fisiknya.
widget: "account_switcher_pattern"_
Pola account switcher: daftar akun yang tersimpan sesi-nya, badge notifikasi per akun tanpa menggabungkan datanya._
Akun IT/Sysadmin murni (tanpa keterlibatan transaksi bisnis) mendapat home berupa System Console (status job, backup, permintaan akses user) yang berbeda kategori sepenuhnya dari home operasional bisnis — bukan sekadar varian KPI dari template yang sama.
— widget: "business_home_vs_admin_home"_
Perbandingan Home akun bisnis (Sales) dengan Home akun IT Admin (System Console) — dua kategori informasi yang berbeda total._
Kamus perilaku — satu pola untuk satu situasi, dipakai persis sama di seluruh aplikasi, dipaksakan lewat komponen bersama (bukan sekadar dokumentasi yang harus diingat manual).
— widget: "interaction_pattern_library"_
Lembar acuan pola interaksi: hierarki tombol, toast, modal konfirmasi destruktif, empty state, loading skeleton._
Cara menjaga konsistensi secara teknis: shared Phoenix function components (<.modal>, <.toast>, <.button variant="...">, <.empty_state>) dengan attr/slot untuk kontrak yang dicek saat compile; katalog visual komponen (mis. PhoenixStorybook); test khusus untuk komponen bersama karena dampak perubahannya luas.
Untuk konteks kantor lokal (LAN, offline), isu klasik field usage (sinyal putus-putus, offline sync) tidak relevan. Yang tetap relevan: kemungkinan ada titik pemakaian fisik berbeda dalam satu lokasi — bukan device berbeda karena di luar kantor, tapi konteks fisik berbeda dalam satu gedung/toko.
— widget: "context_specific_priority"_
Perbandingan prioritas informasi: tabel padat untuk Desktop staf akuntansi vs target sentuh besar untuk Tablet staf gudang._
| Konsep | Label UI (English) |
|---|---|
| Feed perhatian utama di Home | My Attention |
| Saved view — dokumen milik user | Assigned to Me |
| Saved view — menunggu approval | Pending Approval |
| Riwayat dokumen terakhir dibuka | Recent Activity |
| Bagian field yang di-collapse | Advanced Details |
| Tombol simpan tanpa submit | Save Draft |
| Hub konfigurasi/master data | Settings |
Prinsip yang mengikat seluruh keputusan di bawah — bukan sekadar filosofi tersirat, tapi aturan yang tidak boleh dilanggar modul manapun.
| Elemen | Peran |
|---|---|
| Ash.Domain | Bounded context per modul (DDD) — Sales, Inventory, Accounting, dst. |
| Ash Resource | Model — data, validasi, business rule, policy. |
| LiveView (default) / LiveState (khusus POS/kiosk) | View + Controller. LiveView wajib pakai komponen bersama; LiveState untuk titik interaksi yang butuh respons sangat cepat di client atau harus tahan koneksi kurang stabil. |
| Ash.Notifier + AshEvents | Event bus antar domain. Baca data lintas domain = panggilan langsung ke public action; efek samping akibat suatu aksi = event. |
| Reactor | Orkestrasi transaksi “semua-atau-tidak” lintas domain (fork-join Checker, Sales Order → reservasi stok → jurnal). |
DDD sebagai struktur makro (satu Ash.Domain = satu bounded context), tiap domain punya Model + Domain Logic sendiri, tetapi View tetap mengikuti Design System terpusat — bukan MVC penuh yang berdiri sendiri per domain. Folder structure per-domain (lib/bmp/sales/), bukan per-layer teknis.
| Library | Dipakai untuk |
|---|---|
| Reactor | Transaksi butuh rollback/compensation eksplisit. |
| Oban | Job async reliable, terjadwal (eskalasi urgensi approval, dst). |
| AshOban | Job yang terikat lifecycle satu Resource — diprioritaskan di atas Oban mentah bila relevan. |
| Broadway | Ingest data volume/stream dari luar (batch harian POS/E-commerce). |
| NimblePool | Worker pool untuk operasi resource-terbatas (generate PDF/laporan besar, panggilan API eksternal terbatas concurrency). |
Karena hanya satu data layer, tidak ada lagi kebutuhan “test matrix lintas database” — menyederhanakan CI dan menghilangkan kelas bug “jalan di SQLite, gagal di Postgres” (atau sebaliknya) yang sebelumnya jadi risiko di desain dual-layer.
AshArchival + AshPaperTrail, dengan empat aturan wajib:
Lapis keamanan lain (tidak berubah): AshAuthentication (model akun terpisah) → Ash.Policy.Authorizer (otorisasi) → AshCloak (enkripsi field sensitif) → AshRateLimiter (endpoint publik).
TigerBeetle (via TigerBeetlex) — bukan pengganti RDBMS BMP Core, hanya untuk POS & E-commerce standalone. Scope sempit: cuma Account/Transfer (ledger kas) — katalog/sesi/cart tetap SQLite. Terhubung ke BMP Core lewat batch sync harian (Oban scheduled job, konfigurable), longgar — bukan transaksi real-time lintas sistem.
Dengan topologi disederhanakan menjadi SQLite-per-node, kebutuhan shared cache/pub-sub lintas node (Redix) tidak lagi relevan — tiap node (server kantor maupun laptop) berjalan sebagai instance mandiri.
| Komponen | Kapan dipakai |
|---|---|
| ETS | Default satu-satunya — cache in-process ringan, nol dependency tambahan, dipakai di semua node. |
Mnesia dan Redix tidak masuk stack — keduanya menjawab kebutuhan cluster/multi-node yang sudah tidak ada di topologi ini.
Dua profil, disederhanakan dari desain sebelumnya setelah PostgreSQL/cloud dihapus dari scope.
| Profile | Topologi | Node & peran |
|---|---|---|
| 1. Standalone | Satu mesin, satu SQLite | Tidak ada node lain — dipakai UMKM sekecil apa pun (1 komputer/laptop). |
| 2. Office + Mobile Laptop (Local-First Sync) | Satu server kantor on-premise + PC kantor (thin client) + sejumlah laptop mobile | Lihat rincian peran per node di bawah. |
| Node | Instalasi | Sync |
|---|---|---|
| Server kantor | Ash app penuh + SQLite (source of truth kantor) | Menjalankan sync-agent, menunggu koneksi dari laptop mobile |
| PC kantor (desktop, tidak keluar kantor) | Thin client — connect langsung ke server kantor lewat LAN (browser/LiveView biasa) | Tidak perlu — tidak ada SQLite lokal, tidak ada sync-agent |
| Laptop mobile (kadang di luar kantor) | Ash app penuh + SQLite lokal | Menjalankan sync-agent — connect ke localhost saat di luar kantor, ke IP server saat di kantor |
Ini koreksi penting dari desain sebelumnya: bukan semua komputer perlu instalasi penuh + sync-agent. Hanya laptop yang benar-benar berpindah lokasi (dan karenanya butuh kemampuan offline) yang menjalankan SQLite lokal dan sync-agent. PC kantor yang selalu berada di LAN cukup jadi client biasa ke server kantor — persis seperti aplikasi web internal pada umumnya, tanpa kompleksitas sync sama sekali.
Detail lengkap mekanisme sync-agent (komponen, alur, protokol, failure mode, keamanan) dibahas di Addendum.
Tier 1 Ash/Phoenix → Tier 2 Elixir umum → Tier 3 Erlang/OTP langsung. Di mana pun Ash punya ekstensi resmi (Ash*), itu selalu dipilih di atas library generik non-Ash. Non-Ash hanya dipakai untuk hal di luar cakupan Ash (Phoenix itu sendiri, LiveState, Swoosh, Waffle, sync-agent) — semuanya tetap murni ekosistem Erlang/Elixir/Rust resmi tanpa dependency berat di luar itu.
Rincian Library Tier 2 (Elixir Umum) yang Dikunci untuk BMP:
Sync-agent adalah proses Rust kecil yang berjalan di server kantor dan di tiap laptop mobile (bukan di PC kantor — lihat bagian 9). Tugasnya satu: memastikan perubahan data di SQLite laptop dan SQLite server kantor selalu sinkron, tanpa Ash app perlu tahu apa pun.
Diagram 1 — Komponen Sync-Agent
Bertanggung jawab atas capture perubahan dari SQLite: membuat SQLite Session via rusqlite (feature session), attach ke tabel-tabel yang perlu disinkronkan, merekam setiap INSERT/UPDATE/DELETE yang dilakukan Ash app, dan menghasilkan changeset (blob biner) saat flush dipanggil. Session Manager tidak mengintervensi Ash app sama sekali — Ash menulis ke SQLite seperti biasa, Session Extension merekam efeknya di belakang layar.
Bertanggung jawab memutuskan kapan changeset diambil dan dikirim. Model koneksi: begitu laptop pertama kali terhubung ke server kantor, seluruh perubahan tertunda langsung di-flush dan disinkronkan penuh (initial full sync). Setelah itu, koneksi TCP+TLS dijaga tetap terbuka (persistent) — setiap ada perubahan baru, flush dipicu segera (event-driven), bukan menunggu timer. Timer berkala (5–10 detik) tetap dipertahankan sebagai heartbeat/fallback (menjaga koneksi hidup dan menangkap perubahan yang mungkin terlewat), bukan sebagai mekanisme utama.
Bertanggung jawab mengirim dan menerima changeset antar node.
Bertanggung jawab menerapkan changeset yang diterima ke SQLite lokal: memanggil sqlite3changeset_apply() via rusqlite, menjalankan conflict handler per baris per tabel, dan mengirim ACK ke pengirim setelah berhasil.
Bertanggung jawab menyimpan status sync — high_water_mark (offset changeset terakhir yang berhasil di-ACK, berfungsi sebagai sync cursor), schema_version (versi schema SQLite saat ini), last_synced_at, dan peer_status (reachable/unreachable). Disimpan di file lokal kecil terpisah (bukan di SQLite utama) agar tidak tercampur dengan data bisnis.
Mempublikasikan metrik ke Elixir lewat named pipe/Unix socket — event :telemetry (sync.changeset.sent, sync.changeset.applied, sync.conflict.resolved, sync.lag_seconds) yang dikonsumsi System Console BMP untuk menampilkan status sync per node.
Diagram 2 — Alur Lengkap Sync (Laptop → Server → Laptop)
Ash app (exqlite) menulis ke SQLite (WAL mode) seperti biasa — INSERT/UPDATE/DELETE apa pun. Session Manager (rusqlite + session feature) merekam semua perubahan secara otomatis di belakang layar.
Dipicu oleh perubahan baru (event-driven, koneksi persistent), timer heartbeat, peer baru terdeteksi, atau manual trigger. Session Manager memanggil sqlite3session_changeset() dan menghasilkan changeset blob biner.
Transport Layer di laptop mengecek peer reachable, membuka (atau memakai) koneksi TCP+TLS ke server kantor, mengirim schema_version untuk verifikasi, mengirim changeset dalam frame length-prefixed, lalu menunggu ACK.
Server menerima frame, memverifikasi schema_version (tidak cocok → tolak koneksi, kirim error), lalu meneruskan ke Apply Engine yang memanggil sqlite3changeset_apply() dengan conflict handler:
Berhasil → kirim ACK. Gagal → kirim NACK + alasan.
Laptop menerima ACK → update high_water_mark, update last_synced_at, publish telemetry (sync.changeset.sent + applied). Menerima NACK → log error, retry dengan backoff eksponensial, publish telemetry sync.conflict.manual bila perlu.
Setelah ACK terkirim, server melakukan hal yang sama: flush session server (perubahan dari user kantor), kirim changeset ke laptop, laptop apply lalu kirim ACK balik. Ini yang membuat sync dua arah berjalan dalam satu koneksi persistent yang sama — bukan dua koneksi terpisah.
Diagram 3 — Conflict Resolution per Jenis Tabel
Diagram 4 — Failure Mode & Recovery
WAL Mode wajib — semua SQLite yang terlibat harus dikonfigurasi:
PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000;
Tanpa ini, Ash app dan sync-agent bisa saling block saat menulis bersamaan.
CHECK Constraint wajib — karena apply changeset bypass Ash action, business rule kritikal harus ada di level SQLite, bukan hanya di validasi Ash:
CHECK (qty >= 0) CHECK (amount > 0)
Schema Version wajib — setiap migrasi Ash harus menaikkan schema_version di metadata database. Sync-agent membaca ini sebelum menerima koneksi apa pun, dan menolak sync jika versi laptop dan server tidak cocok (urutan upgrade: server kantor dulu, baru laptop — selama laptop belum di-upgrade, sync berhenti sementara, bukan corrupt).
Belum disusun — akan dibahas pada sesi berikutnya.
Cakupan yang direncanakan:
Belum disusun — akan dibahas pada sesi berikutnya.
Cakupan yang direncanakan:
Belum disusun — akan dibahas pada sesi berikutnya.
Cakupan yang direncanakan:
Belum disusun — akan dibahas pada sesi berikutnya. Sales sudah dipakai sebagai contoh acuan pada sesi-sesi sebelumnya.
Cakupan yang direncanakan:
Belum disusun — akan dibahas pada sesi berikutnya. Modul-modul ini dirancang togglable per instalasi dan dapat didorong ke kontributor komunitas.
Cakupan yang direncanakan: