Semua karya

Tempo

Pencatatan waktu, persetujuan, dan invoice untuk studio kecil, dalam satu ruang kerja.

Tahun
2026
Peran
Desain dan pembangunan
Jenis
Produk · API dan aplikasi web
Stack
.NET 10 · PostgreSQL · Next.js 15 · React 19 · Tailwind v4 · Kubernetes
Ringkasan dengan jam mingguan per klien, utilisasi, dan nilai belum ditagih (data demo)

Masalah

Freelancer dan agensi mencatat jam di satu alat, menyetujuinya di alat lain, dan membuat invoice dari alat ketiga. Tempo menyambung lingkaran itu: proyek, tugas, timesheet, persetujuan, invoice, dan portal klien dalam satu tempat.

Batasan

  • Pemisahan arsitektur harus mempertahankan setiap route, status code, dan bentuk JSON.
  • Riwayat migrasi tetap linear; tidak ada penulisan ulang migrasi yang sudah rilis.
  • Izin multi-workspace dengan pemeriksaan server-side di setiap endpoint.
  • Satu target deploy: klaster k3s homelab, disajikan melalui satu ingress.

Sistem

API
.NET 10 minimal API yang dibagi menjadi Domain, Shared, Application, Infrastructure, dan Api. Aturan bisnis berupa static class dengan unit test; handler memakai EF Core langsung, tanpa lapisan repository.
Data dan job
PostgreSQL dengan migrasi EF Core yang dijalankan saat startup di balik advisory lock. Outbox dispatcher menangani email; overdue sweep menandai invoice. Persetujuan timesheet adalah state machine, bukan sekadar kolom status.
Klien
Next.js 15 App Router dengan React 19, Tailwind v4, dan primitif Radix. Portal klien dan ruang kerja internal berbagi komponen, tetapi tidak berbagi izin.

Keputusan

  1. 1.

    Aturan di static class, bukan service

    Total invoice, pembulatan waktu, dan state machine tugas adalah fungsi murni. Integration test mencakup kontrak HTTP; unit test mencakup aritmetika dan transisi.

    Tradeoff Lebih sedikit indirection, tetapi handler harus tetap tipis atau aturan bocor ke dalamnya.

  2. 2.

    Outbox, bukan email langsung

    Penulisan menyimpan pesan dalam transaksi yang sama dengan perubahan bisnis. Dispatcher mencoba ulang di luar request.

    Tradeoff Satu tabel dan background worker tambahan untuk dioperasikan.

  3. 3.

    Satu workspace per request

    CurrentWorkspace diselesaikan sekali dan diwajibkan di setiap endpoint. Tidak ada tenant ambien di koneksi database.

    Tradeoff Setiap query harus membawa predikat workspace; test menegakkannya.

  4. 4.

    Migrasi saat startup

    Advisory lock PostgreSQL menyerialkan migrasi di antara replika. Deploy tetap satu langkah.

    Tradeoff Migrasi yang buruk menjatuhkan API; lock hanya mencegah balapan.

Layar

Timesheet mingguan per hari (data demo)Daftar tugas dengan timer per tugas (data demo)

Status

Berjalan di deployment dev dari klaster k3s homelab. Screenshot memakai workspace demo. Repositori privat.

Bukti