Membangun Platform Keanggotaan APMNU: Dari Dokumen Organisasi ke Satu Sumber Data
Catatan membangun platform APMNU yang menyatukan website publik, portal profesor, panel admin, dan REST API—termasuk keputusan domain, monorepo, serta CI/CD.
Membangun Platform Keanggotaan APMNU: Dari Dokumen Organisasi ke Satu Sumber Data
Membangun website organisasi sering terlihat seperti pekerjaan membuat halaman profil, berita, dan kontak. Dalam proyek Asosiasi Profesor Muslimat Nahdlatul Ulama (APMNU), kebutuhan sebenarnya jauh lebih besar.
Organisasi memerlukan tempat untuk memperkenalkan diri kepada publik, tetapi juga membutuhkan proses pendaftaran anggota, verifikasi SK Guru Besar, direktori profesor, pengelolaan profil akademik, agenda, liputan, dan ruang operasional untuk pengurus.
Artinya, yang kami bangun bukan hanya sebuah website. Kami membangun platform keanggotaan dengan tiga sisi produk:
- website publik untuk masyarakat;
- portal mandiri untuk profesor;
- panel admin untuk pengurus organisasi.
Saya mengerjakan proyek ini sebagai Project Manager sekaligus Full-Stack Developer bersama tim beranggotakan tiga developer. Dalam artikel ini, saya ingin membahas proses berpikir di balik produknya: bagaimana kebutuhan organisasi diterjemahkan menjadi domain, mengapa kami memilih monorepo, dan keputusan apa saja yang membuat sistem lebih mudah dikembangkan serta dioperasikan.

Masalahnya Bukan Sekadar “Belum Punya Website”
Data guru besar perempuan NU sebelumnya belum tersedia sebagai direktori publik yang mudah ditelusuri. Proses keanggotaan dan verifikasi juga membutuhkan alur yang konsisten, sedangkan informasi organisasi dipublikasikan melalui kanal yang terpisah.
Dari discovery awal, kami menemukan tiga kelompok pengguna dengan kebutuhan berbeda.
Publik
Publik perlu memahami profil APMNU, melihat agenda dan liputan, serta menemukan profesor berdasarkan informasi akademiknya. Dalam konteks tertentu, pengunjung juga perlu menghubungi profesor sebagai calon narasumber.
Profesor
Profesor membutuhkan proses pendaftaran yang jelas dan ruang untuk mengelola data akademiknya sendiri. Informasi seperti riwayat pendidikan, bidang keahlian, publikasi, dan tautan akademik akan lebih akurat jika pemilik profil dapat memperbaruinya langsung.
Pengurus
Pengurus memerlukan panel untuk meninjau pendaftaran, memastikan dasar keanggotaan valid, serta mengelola agenda, liputan, materi, galeri, dan halaman organisasi tanpa bergantung kepada developer untuk perubahan konten harian.
Jika ketiga kebutuhan itu dibangun sebagai sistem terpisah, data akan kembali terfragmentasi. Karena itu, tujuan utama kami adalah membuat satu sumber data yang melayani kebutuhan publik dan internal sekaligus.
Mengubah Kebutuhan Menjadi Alur Produk
Kami menyusun alur keanggotaan seperti berikut:
Calon anggota
│ mengirim identitas + SK Guru Besar
▼
Pendaftaran
│
▼
Review admin
├── Disetujui
├── Perlu revisi + catatan
└── Ditolak + alasan
│
▼
Portal profesor
│ melengkapi profil akademik
▼
Direktori publik terverifikasi
Alur tersebut terlihat sederhana setelah ditulis, tetapi ada keputusan domain penting di dalamnya: status pendaftaran bukan status keanggotaan.
Kami memakai dua kelompok status yang berbeda:
- status registrasi:
PENDING,APPROVED,REJECTED, danNEED_REVISION; - status keanggotaan:
ACTIVE,EMERITUS, danDECEASED.
Pemisahan ini mencegah pertanyaan yang ambigu. Seseorang bisa sudah lolos verifikasi registrasi, tetapi kondisi keanggotaannya berubah di kemudian hari. Jika keduanya disimpan dalam satu field, aturan bisnis akan cepat membingungkan dan rawan salah digunakan.
Dari proyek ini saya kembali belajar bahwa banyak bug bisnis sebenarnya bermula dari kosakata domain yang belum disepakati, bukan dari syntax atau framework.
Arsitektur: Tiga Aplikasi dalam Satu Monorepo
Kami menggunakan monorepo TypeScript berbasis Turborepo dengan struktur utama seperti berikut:
apmnu/
├── apps/
│ ├── api/ # NestJS REST API
│ ├── web/ # website publik + portal profesor, Nuxt 4
│ └── admin/ # panel pengurus, Nuxt 4
└── packages/
├── schemas/ # kontrak validasi Zod
├── types/ # TypeScript types bersama
├── ui/ # komponen Vue bersama
├── utils/ # fungsi utilitas
└── config-*/ # konfigurasi TypeScript dan ESLint
Alur komunikasinya:
Publik ───────────────► Website Nuxt
Profesor ─────────────► Website Nuxt ──┐
│
▼
NestJS API ──► PostgreSQL
▲ Prisma
│
Pengurus ─────────────► Panel Admin ────┘
Shared Zod schemas digunakan di semua aplikasi
Mengapa panel admin dipisahkan?
Pilihan termudah sebenarnya adalah menaruh halaman admin di aplikasi web yang sama lalu melindunginya dengan role. Kami memilih aplikasi terpisah karena kebutuhan keduanya berkembang ke arah berbeda.
Website publik berorientasi pada kecepatan, kemudahan eksplorasi, dan pengalaman pengguna. Panel admin lebih padat data, memiliki form kompleks, tabel, review dokumen, serta aksi operasional.
Pemisahan ini memberi beberapa manfaat:
- bundle website publik tidak membawa fitur administratif;
- batas autentikasi dan tanggung jawab aplikasi lebih jelas;
- web dan admin dapat dikembangkan serta dirilis secara independen;
- perubahan dashboard tidak harus mengganggu pengalaman publik.
Trade-off-nya adalah setup awal lebih besar dan sebagian komponen perlu dibagikan melalui packages/ui. Namun, biaya tersebut masuk akal karena kedua aplikasi memang memiliki arah pengembangan yang berbeda.
Shared Zod Schema sebagai Kontrak
Ketika satu API digunakan oleh dua frontend, duplikasi validasi mudah terjadi. Backend memiliki aturannya sendiri, website publik menulis ulang aturan serupa, lalu panel admin membuat versi ketiga. Seiring waktu, ketiganya dapat menyimpang.
Untuk mengurangi masalah itu, kami menaruh Zod schema bersama di packages/schemas. API, website, dan admin menggunakan definisi yang sama untuk struktur request maupun aturan validasi yang relevan.
Secara konseptual:
// packages/schemas
export const registrationSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
// field pendaftaran lainnya
});
Schema tersebut kemudian dapat digunakan di sisi form dan API. Hasilnya bukan hanya lebih sedikit duplikasi, tetapi juga perubahan kontrak menjadi lebih terlihat. Ketika sebuah field berubah, aplikasi yang terdampak dapat ditemukan melalui typecheck dan build.
Bagi saya, shared schema adalah salah satu manfaat paling nyata dari monorepo TypeScript pada produk multi-aplikasi.
Menentukan Batas Verifikasi
Kami menetapkan SK Guru Besar sebagai dasar utama verifikasi keanggotaan. Setelah pendaftaran disetujui, profesor dapat memperbarui informasi akademik non-verifikasi secara mandiri.
Keputusan ini merupakan kompromi antara kontrol dan kemudahan operasi.
Jika setiap perubahan profil harus menunggu approval admin, data memang lebih terkontrol, tetapi pengurus menjadi bottleneck. Sebaliknya, jika seluruh data bebas diubah tanpa batas yang jelas, dasar keanggotaan ikut kehilangan kepastian.
Dengan memisahkan data verifikasi dan data profil:
- dokumen yang menentukan keabsahan anggota tetap ditinjau;
- profesor memiliki kepemilikan atas profil akademiknya;
- admin tidak dibebani approval untuk setiap perubahan kecil;
- direktori dapat terus diperbarui tanpa proses birokrasi tambahan.
Tidak Semua Fitur Perlu Dibangun
Salah satu kebutuhan adalah memungkinkan publik menghubungi profesor sebagai narasumber. Kami bisa saja membuat inbox internal lengkap dengan thread, notifikasi, status pesan, dan moderasi.
Namun, kebutuhan yang sebenarnya adalah membuka jalur kontak, bukan menciptakan platform komunikasi baru. Karena itu, aksi “Undang Narasumber” diarahkan ke email profesor.
Kami kehilangan kemampuan tracking percakapan di dalam sistem, tetapi menghindari pembangunan satu modul besar yang belum terbukti akan digunakan. Ini adalah contoh penting bahwa keputusan produk yang baik tidak selalu berupa penambahan fitur. Kadang keputusan terbaik adalah menetapkan batas dan tidak membangun sesuatu terlalu cepat.
Fitur yang Akhirnya Terbentuk
Platform APMNU mencakup beberapa kemampuan utama:
- direktori profesor publik dengan pencarian dan filter;
- registrasi anggota dengan proses review dan revisi;
- portal self-service untuk profil akademik;
- kartu anggota digital bagi profesor yang telah disetujui;
- dashboard admin dengan statistik dan daftar pendaftaran terbaru;
- pengelolaan agenda dan liputan organisasi;
- materi akademik, galeri, dan halaman profil organisasi;
- rich text editor untuk pengelola konten non-teknis;
- dokumentasi REST API melalui Swagger.
Pada halaman publik, konten agenda dibuat visual dan responsif agar tetap nyaman dilihat dari perangkat mobile.

Menjalankan Peran PM dan Developer Bersamaan
Dalam proyek ini, saya tidak hanya menerima task implementasi. Saya juga menerjemahkan kebutuhan klien menjadi PRD, menyusun backlog, membagi pekerjaan, menjalankan sprint planning dan weekly review, serta menjaga agar keputusan produk tidak terlepas dari kondisi teknis.
Kami awalnya menggunakan Linear, kemudian memindahkan tracking pekerjaan ke Jira. Migrasi tool di tengah proyek berisiko membuat konteks hilang, sehingga yang perlu dipindahkan bukan hanya judul issue, tetapi juga acceptance criteria, hubungan antartask, keputusan sebelumnya, dan status pekerjaan yang sebenarnya.
Menjalankan peran PM sekaligus developer memberi keuntungan: saya dapat melihat lebih cepat ketika sebuah kebutuhan memiliki konsekuensi besar pada domain atau arsitektur. Namun, ada risiko terlalu fokus pada detail implementasi. PRD, backlog yang rapi, dan ritme review membantu saya tetap menjaga pandangan produk secara keseluruhan.
Pelajaran terbesarnya adalah bahwa dokumentasi bukan pekerjaan setelah coding selesai. Dokumentasi adalah alat untuk membuat tim bergerak dengan pemahaman yang sama.
CI/CD: Rilis yang Bisa Dilacak dan Diulang
Ketiga aplikasi dikemas menjadi image Docker terpisah dan disimpan di GitHub Container Registry. Image diberi tag berdasarkan commit SHA, bukan hanya latest.
Alur sederhananya:
Push / workflow dispatch
│
▼
Lint + typecheck + build
│
▼
Build image aplikasi yang berubah
│
▼
Push ke GHCR dengan tag commit SHA
│
▼
Deploy ke staging atau production
Tag commit SHA membuat artefak rilis dapat ditelusuri kembali ke source code yang tepat. Jika versi tertentu bermasalah, rollback dapat dilakukan dengan menunjuk image sebelumnya tanpa build ulang di server.
Kami juga menggunakan deteksi perubahan berbasis path. Perubahan yang hanya menyentuh panel admin tidak perlu memicu seluruh proses untuk API dan website publik. Selain menghemat waktu CI, pola ini menjaga pipeline tetap relevan dengan batas aplikasi di dalam monorepo.
Hal yang Paling Sulit
Bagian tersulit bukan memilih Nuxt, NestJS, atau Prisma. Tantangan terbesarnya adalah menjaga agar pemahaman produk tetap konsisten di antara kebutuhan organisasi, model data, API, tampilan untuk tiga jenis pengguna, dan pekerjaan beberapa developer.
Beberapa hal yang paling membantu:
-
Menyepakati istilah domain lebih awal. Nama yang tepat mengurangi ambiguitas pada database, endpoint, UI, dan diskusi tim.
-
Menulis alasan di balik keputusan. Dokumentasi arsitektur menjawab “mengapa dibuat seperti ini”, bukan hanya “bagaimana cara menjalankannya”.
-
Menjadikan schema dan typecheck sebagai quality gate. Kontrak yang rusak perlu ditemukan sebelum deployment.
-
Mendesain untuk pergantian pengurus. Struktur data, nama menu, dan dokumentasi tidak boleh bergantung pada satu orang yang sedang menjabat.
-
Membedakan kebutuhan nyata dari solusi yang terlalu besar. Email langsung dapat menjadi keputusan yang lebih baik daripada membangun messaging internal terlalu dini.
Penutup
Proyek APMNU mengajarkan saya bahwa membangun sistem organisasi berarti merancang hubungan antara data, proses verifikasi, pengguna publik, dan operasi internal—bukan sekadar menyusun halaman.
Monorepo membantu kami menjaga kontrak lintas aplikasi. Pemisahan status membuat domain lebih tepat. Portal self-service mengurangi beban pengurus. Pipeline berbasis image membuat rilis dapat diulang dan dilacak. Di atas semua itu, PRD dan dokumentasi menjaga tim tetap menyelesaikan masalah yang sama.
Hasil akhirnya adalah satu platform yang menghubungkan website publik, keanggotaan profesor, dan pekerjaan pengurus tanpa kembali menciptakan silo data baru.
Versi ringkas proyek, diagram arsitektur, stack, dan kontribusi saya dapat dilihat di case study APMNU. Website publiknya tersedia di apmnu.org, sedangkan source code dapat dilihat di GitHub.