Dokumentasi Pagonila

Dokumentasi dibagi menjadi beberapa bagian.

FiturDokumentasi
DBMLDBML Docs
FlowchartFlowchart Docs
API SpecificationAPI Specification Docs
LibraryLibrary Docs
Library/Data ModelData Model Docs
Library/Engine LibraryEngine Library Docs
Library/Driver LibraryDriver Library Docs

Apa itu Pagonila

Pagonila adalah sistem perancangan Perangkat Lunak berbasis API. Pengguna menulis rancangan perangkat lunak dalam bentuk desain basis data, alur proses, dan spesifikasi API. Hasil rancangan tersebut akan menghasilkan pustaka/library yang dapat digunakan sebagai backend dan driver pada frontend.

Motivasi

Misalkan sebuah website Toko Online sedang dalam proses pengembangan. Website tersebut kurang-lebih akan memiliki kebutuhan sebagai berikut:

KebutuhanImplementasi
FrontendReactJS
User SessionLocal Storage/Cookies
HTTP RequestAxios/Fetch
Backend FrameworkExpressJS
Request Validator/TransformClass Validator / Class Transform
ORM/Data ModelTypeORM
SQL Script MigrationTypeORM Migrations
DatabasePostgreSQL

Frontend

Pada bagian ReactJS, bentuk implementasi mungkin akan seperti ini:

frontend/model.ts
export interface Product {
  id: number
  title: string
  image: string
  description?: string
  price: number
}
 
export interface User {
  id: number
  name: string
  email: string
}
 
export interface LoginRequest {
  email: string
  password: string
}
frontend/pages/login.tsx
import { LoginRequest } from '../model';
 
export default function LoginPage() {
  const [data, setData] = useState<LoginRequest>({ email: '', password: '' });
 
  function submitLogin() {
    // ... call API login, lalu simpan JWT token ke local storage/cookies
  }
 
  return (
    <div>
      <input type={'email'} value={data.email} onChange={e => setData({ ...data, email: e.target.value })} />
      <input type={'password'} value={data.password} onChange={e => setData({ ...data, password: e.target.value })} />
      <button onClick={submitLogin}>Login</button>
    </div>
  );
}
frontend/pages/homepage.tsx
import { Product } from '../model';
 
export default function HomePage() {
  const [list_product, setListProduct] = useState<Product[]>([]);
 
  function initProduct() {
    // ... call API product, set state list produk
  }
 
  useState(() => {
    initProduct();
  }, []);
 
  return (
    <div className={`grid grid-cols-3 gap-[8px]`}>
      {
        list_product.map((p: Product) => (
          <ProductCard key={p.id} data={p} />
        ))
      }
    </div>
  );
}

Backend

Lalu pada backend ExpressJS, implementasi mungkin berbentuk seperti berikut ini:

backend/index.ts
import express from 'express';
import loginController from 'fn/login';
import productListController from 'fn/product-list';
 
const app = express();
app.post('/login', loginController);
app.get('/product', productListController);
 
app.listen(3000, () => { console.log('Express App running on port 3000') });
backend/model.ts
export interface User {
  id: number
  name: string
  email: string
}
 
export interface Product {
  id: number
  title: string
  image: string
  description?: string
  price: number
}
 
export interface LoginResponse {
  token: string
  user: User
}
 
export type ProductListResponse = Product[];
backend/validator.ts
import { Length, IsEmail, IsNotEmpty } from 'class-validator';
 
export class LoginRequest {
  @IsNotEmpty()
  @IsEmail()
  email: string;
 
  @IsNotEmpty()
  @Length(8, 36)
  password: string;
}
 
export class ProductListRequest {
  @IsNotEmpty()
  authorization: string;
}
backend/fn/login.ts
import { Request, Response } from 'express';
import { validateOrReject } from 'class-validator';
import { LoginRequest } from '../validator';
 
export async function loginController(req: Request, res: Response) {
  const login_request = new LoginRequest(req.body);
  try {
    await validateOrReject(login_request);
  } catch (err: any) {
    res.status(400).send(`Email atau password tidak valid`);
    return;
  }
 
  const user = await ORM.getUser({ email: login_request.email });
  if (!user) {
    res.status(400).send(`Email tidak ditemukan`);
    return;
  }
 
  if (!hashOK(login_request.password, user.password)) {
    res.status(400).send(`Password salah`);
    return;
  }
 
  const response: LoginResponse = {
    user, token: signJWT(user.id)
  };
 
  res.json(response);
}
backend/fn/product-list.ts
import { Request, Response } from 'express';
import { validateOrReject } from 'class-validator';
import { ProductListRequest } from '../validator';
 
export async function productListController(req: Request, res: Response) {
  const request_data = new ProductListRequest(req.headers);
  try {
    await validateOrReject(request_data);
  } catch (err: any) {
    res.status(400).send(`Request invalid`);
    return;
  }
 
  // periksa apakah token authorization valid
  await extractJWT(request_data.authorization);
 
  const response: ProductListResponse = await ORM.getListProduct();
  res.json(response);
}

Efisiensi Kode

Pada contoh implementasi di atas, terdapat beberapa kode yang ditulis berulang pada dua platform berbeda. Data berulang seperti model untuk request dan response API dan model basis data (misal: User) seharusnya dapat ditulis sekali saja. Spesifikasi API dan validasi request juga dapat diefisiensikan pembuatannya sehingga cukup ada satu sumber data yang digunakan oleh backend dan frontend tanpa harus menulis/mendaftarkan API di kedua platform.

Permasalahan efisiensi di atas dapat ditangani dengan membuat sebuah sumber data terpusat yang dapat digunakan dengan berbagi data antara frontend dan backend. Perlu diperhatikan kode yang dibagikan ke kedua platform harus konsisten dan kompatibel.

Dokumentasi Data & Proses

Bagaimana seorang programmer dapat melakukan transfer knowledge sebuah sistem ke programmer lain dengan cepat?

Misalkan sebuah website yang cukup besar seperti "Toko Online", terdapat beberapa halaman unik pada website tersebut dengan proses yang cukup bervariasi di setiap halaman. Beberapa programmer yang saya temui akan mulai membahas tentang dokumentasi teknis.

Kejadian di lapangan ternyata cukup sulit dan memakan waktu untuk membaca dokumentasi teknis. Kebanyakan karena interpretasi setiap orang dan kalimat dalam menuliskan penjelasan di dokumentasi berbeda pada setiap programmer. Akan menjadi lebih sulit lagi ketika terdapat perbedaan preferensi dalam penulisan kode pada dokumentasi tersebut.

Data sebaiknya di-dokumentasikan pada sebuah bentuk relasional antar tabel. Terlepas bentuk data SQL atau no-SQL, setiap model data yang ada pada sebuah sistem kemungkinan besar akan memiliki hubungan dengan model data lain. Proses sebaiknya di-dokumentasikan dalam bentuk abstraksi berupa flowchart dan memfokuskan bagian-bagian penting dari proses tersebut.

Solusi

Pagonila merangkum seluruh ketidakefisienan dari permasalahan di atas dan menghasilkan sebuah sistem terpusat yang mendokumentasikan dan menyediakan library (pustaka) sistem yang siap digunakan sebagai backend dan driver frontend.

Pengguna mendefinisikan model data dan proses dalam bentuk DBML dan flowchart. Data tersebut diolah ke dalam kerangka spesifikasi API. Kerangka dari spesifikasi API ini kemudian dilengkapi hingga menjadi spesifikasi API. Model data dan spesifikasi API yang telah lengkap kemudian di-deploy menjadi sebuah library yang siap digunakan.

DBML Editor

Pagonila menyediakan editor untuk merancang skema basis data. Pengguna memasukan kode DBML lalu akan menghasilkan skema basis data pada kanvas editor. Penjelasan lebih lanjut tentang DBML dapat dilihat di halaman resmi https://dbml.dbdiagram.io/home.

Lihat selengkapnya Dokumentasi DBML Editor.

Flowchart Builder

Flowchart builder yang ada pada Pagonila bertujuan untuk mendokumentasikan proses dari program. Flowchart ini berbeda dari cara dokumentasi program pada umumnya seperti menggunakan Markdown, komentar pada kode, ataupun halaman dokumen yang di-generate sistem seperti Doxygen, Javadoc, dll. Flowchart pada Pagonila lebih berfokus pada proses yang terjadi pada frontend terkait dengan proses pemanggilan data melalui API secara umum.

Seluruh proses API pada flowchart kemudian dikompilasi menjadi sebuah kerangka spesifikasi API pada tahap API Specification.

Lihat selengkapnya Dokumentasi Flowchart Builder.

API Specification Editor

Pada API Specification Editor kerangka spesifikasi API di-generate oleh sistem berdasarkan proses Flowchart Builder. Spesifikasi API tersebut terhubung dengan data pada DBML Editor. Pengguna perlu melengkapi data API seperti parameters, return value, atau schema sesuai kebutuhan API.

Lihat selengkapnya Dokumentasi API Specification.

Pustaka/Library

Setelah spesifikasi API dilengkapi, pengguna kemudian men-deploy spesifikasi tersebut menghasilkan sebuah pustaka/library. Library ini terdiri dari tiga bagian besar:

Penutup

Pagonila berusaha membuat pengguna menuliskan seluruh dokumentasi sebuah program mulai dari model basis data hingga spesifikasi API. Dokumentasi ini kemudian dipublikasikan menjadi sebuah library yang dapat diimplementasi dan digunakan pada kode program sistem yang sedang dikerjakan. Produk keluaran dari sistem Pagonila pada akhirnya ada dua:

  • Dokumentasi Sistem, dan
  • Pustaka/Library program.
Outline