<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Andriawan]]></title><description><![CDATA[Tech Enthusiast]]></description><link>https://verri.andriawan.web.id</link><generator>RSS for Node</generator><lastBuildDate>Fri, 17 Apr 2026 09:25:11 GMT</lastBuildDate><atom:link href="https://verri.andriawan.web.id/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Singleton Pattern]]></title><description><![CDATA[Pada article tentang NestJS Framework sempat menyinggung bahwa secara default Decorator Injectable akan mendeklarasikan instance sebagai Singleton Object. Tapi apa itu Singleton Object. Mengapa digunakan sebagai default dari decorator Injectable dari...]]></description><link>https://verri.andriawan.web.id/singleton-pattern</link><guid isPermaLink="true">https://verri.andriawan.web.id/singleton-pattern</guid><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Tue, 17 Feb 2026 17:01:20 GMT</pubDate><content:encoded><![CDATA[<p>Pada article tentang <a target="_blank" href="https://verri.andriawan.web.id/nestjs-framework">NestJS Framework</a> sempat menyinggung bahwa secara default <code>Decorator Injectable</code> akan mendeklarasikan instance sebagai Singleton Object. Tapi apa itu Singleton Object. Mengapa digunakan sebagai default dari decorator <code>Injectable</code> dari NestJS dan resiko apa yang mungkin terjadi.</p>
<p>Ketika developer membuat aplikasi, perlu dengan hati-hati menghitung beban resource dari aplikasi yang dibangunnya, salah satunya setiap kali membuat object, memerlukan sebuah object yang hanya diinialisasi sekali dengan tujuan agar tidak memakan banyak resource dan bisa dipakai secara global selama aplikasi berjalan. Maka developer bisa mengandalkan singleton pattern untuk hal tersebut.</p>
<p>Pada umumnya, Singleton Object biasa digunakan untuk beberapa hal seperti:</p>
<ul>
<li><p>Configuration managers</p>
</li>
<li><p>Database Pool Connection</p>
</li>
<li><p>Logging service</p>
</li>
<li><p>Cache Mechanisms</p>
</li>
<li><p>Thread Pools</p>
</li>
</ul>
<p>Sebagai contoh kasus sederhana dan naive seperti misal untuk logging service kecil ini, dimana seandainya destination dari logging adalah sebuah file dengan nama <code>service_&lt;runtime_timestamp&gt;.log</code> dan variable <code>runtime_filename</code> di-store di object.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { appendFileSync } <span class="hljs-keyword">from</span> <span class="hljs-string">'fs'</span>;


<span class="hljs-comment">/// log module</span>
<span class="hljs-keyword">class</span> LogModule {
    <span class="hljs-keyword">private</span> logFile: <span class="hljs-built_in">string</span> = <span class="hljs-string">''</span>;

    <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
        <span class="hljs-keyword">const</span> timestamp = <span class="hljs-built_in">Date</span>.now();
        <span class="hljs-built_in">this</span>.logFile = <span class="hljs-string">`service_<span class="hljs-subst">${timestamp}</span>`</span>;        
    }

    <span class="hljs-keyword">static</span> info(msg: <span class="hljs-built_in">string</span>) {
        <span class="hljs-keyword">const</span> inst = <span class="hljs-keyword">new</span> LogModule
        <span class="hljs-keyword">return</span> inst.log(<span class="hljs-string">'info'</span>, msg)
    }

    log(<span class="hljs-keyword">type</span>: <span class="hljs-built_in">string</span>, msg: <span class="hljs-built_in">string</span>) {
        appendFileSync(<span class="hljs-built_in">this</span>.logFile, msg);
    }
}


<span class="hljs-comment">// Service B</span>
<span class="hljs-keyword">class</span> AService {
   <span class="hljs-keyword">static</span> run() {
       LogModule.info(<span class="hljs-string">'Message service A'</span>)
   }
}

<span class="hljs-comment">// Service B</span>
<span class="hljs-keyword">class</span> BService {
   <span class="hljs-keyword">static</span> run() {
      LogModule.info(<span class="hljs-string">'Message service B'</span>)
   }
}

<span class="hljs-comment">// Main</span>
AService.run()
BService.run()
</code></pre>
<p>Jika code tersebut dijalankan, yang terjadi adalah akan tebuat lebih dari satu file log dengan timestamp filename yang berbeda, mengapa? karena setiap kali module service dijalankan, maka akan membuat object log module baru juga yang mana pada <code>constructor</code> pada <code>LogModule</code> akan mengenerate <code>fileLog</code> dengan <code>timestamp</code> yang baru. Akibatnya dengan terbuatanya Log Module yang berbeda akan berakibat terbuatnya file log yang berbeda. Dari masalah kasus <code>LogModule</code> tersebut, maka akan cocok menggunakan Singleton Creational Pattern.</p>
<p>Kita coba implementasikan Singleton Pattern pada <code>LogModule</code> seperti berikut</p>
<pre><code class="lang-typescript">
<span class="hljs-keyword">class</span> LogModule {
    <span class="hljs-keyword">static</span> instance: LogModule | <span class="hljs-literal">null</span>;
    <span class="hljs-keyword">private</span> logFile: <span class="hljs-built_in">string</span> = <span class="hljs-string">''</span>;

    <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
        <span class="hljs-keyword">const</span> timestamp = <span class="hljs-built_in">Date</span>.now();
        <span class="hljs-built_in">this</span>.logFile = <span class="hljs-string">`service_<span class="hljs-subst">${timestamp}</span>`</span>;        
    }

    <span class="hljs-comment">// static info(msg: string) {</span>
    <span class="hljs-comment">//    const inst = new LogModule</span>
    <span class="hljs-comment">//    return inst.log('info', msg)</span>
    <span class="hljs-comment">// }</span>

    <span class="hljs-keyword">static</span> init() {
        <span class="hljs-keyword">if</span> (!LogModule.instance) {
            <span class="hljs-keyword">const</span> inst = <span class="hljs-keyword">new</span> LogModule;
            LogModule.instance = inst;
        }

        <span class="hljs-keyword">return</span> LogModule.instance;
    }

    <span class="hljs-comment">// log(type: string, msg: string) {</span>
    <span class="hljs-comment">//    appendFileSync(this.logFile, msg);</span>
    <span class="hljs-comment">// }</span>

    info(msg: <span class="hljs-built_in">string</span>) {
        appendFileSync(<span class="hljs-built_in">this</span>.logFile, <span class="hljs-string">`<span class="hljs-subst">${msg}</span>\n`</span>);
    }
}
</code></pre>
<p>Seperti terlihat pada snippet code di atas, kita mengganti <code>static info</code> method dengan <code>static init</code> method. Lalu pada content method tersebut kita menambahkan conditional checker jika <code>property static instance</code> bernilai <code>null</code> atau belum terisi nilai apapun, maka buat instance baru dan simpan pada <code>property static instance</code> lalu return nilai tersebut.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">if</span> (!LogModule.instance) {
    <span class="hljs-keyword">const</span> inst = <span class="hljs-keyword">new</span> LogModule;
    LogModule.instance = inst;
}
</code></pre>
<p>Sehingga ketika kita mengeksekusi method <code>LogModule.init</code>, kita tidak membuat instance baru dan tidak menjalankan <code>constructor</code> pada <code>LogModule</code>. Kita akan mendapatkan reference dari instance yang sudah dibuat sebelumnya.</p>
<p>Code setelah mengimplementasikan Singleton Pattern akan seperti ini.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { appendFileSync } <span class="hljs-keyword">from</span> <span class="hljs-string">'fs'</span>;

<span class="hljs-keyword">class</span> LogModule {
    <span class="hljs-keyword">static</span> instance: LogModule | <span class="hljs-literal">null</span>;
    <span class="hljs-keyword">private</span> logFile: <span class="hljs-built_in">string</span> = <span class="hljs-string">''</span>;

    <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
        <span class="hljs-keyword">const</span> timestamp = <span class="hljs-built_in">Date</span>.now();
        <span class="hljs-built_in">this</span>.logFile = <span class="hljs-string">`service_<span class="hljs-subst">${timestamp}</span>`</span>;        
    }

    <span class="hljs-keyword">static</span> init() {
        <span class="hljs-keyword">if</span> (!LogModule.instance) {
            <span class="hljs-keyword">const</span> inst = <span class="hljs-keyword">new</span> LogModule;
            LogModule.instance = inst;
        }

        <span class="hljs-keyword">return</span> LogModule.instance;
    }

    info(msg: <span class="hljs-built_in">string</span>) {
        appendFileSync(<span class="hljs-built_in">this</span>.logFile, <span class="hljs-string">`<span class="hljs-subst">${msg}</span>\n`</span>);
    }
}

<span class="hljs-comment">// Service B</span>
<span class="hljs-keyword">class</span> AService {
   <span class="hljs-keyword">static</span> run() {
       LogModule.init().info(<span class="hljs-string">'Message service A'</span>)
   }
}

<span class="hljs-comment">// Service B</span>
<span class="hljs-keyword">class</span> BService {
   <span class="hljs-keyword">static</span> run() {
      LogModule.init().info(<span class="hljs-string">'Message service B'</span>)
   }
}

<span class="hljs-comment">// Main</span>
AService.run()
BService.run()
</code></pre>
<p>Dengan begitu, sebanyak apapun kita menjalankan method init pada LogModule tersebut, LogModule tidak akan membuat object baru. Hal tersebut akan meringankan beban penggunaan memory pada aplikasi.</p>
<p>Beberapa keunggulan dari Singleton Creational Pattern</p>
<ul>
<li><p>Bisa dipastikan bahwa class atau module tersebut hanya memiliki satu instance.</p>
</li>
<li><p>Bisa akses secara global dimodule manapun, dengan syarat tidak memiliki mutable property pada class atau module tersebut</p>
</li>
<li><p>Resource effisient</p>
</li>
</ul>
<p>Selain itu juga ada kekurangannya</p>
<ul>
<li><p>Bisa ada hidden dependency</p>
</li>
<li><p>Bisa jadi bermasalah pada concurency jika tidak hati-hati</p>
</li>
<li><p>Unittest atua automatic testing akan cukup sulit, tapi bisa diatasi dengan dependency injection</p>
</li>
</ul>
<p><strong>references</strong></p>
<ul>
<li><p><a target="_blank" href="https://www.geeksforgeeks.org/system-design/gang-of-four-gof-design-patterns">Gang of 4 Design Patterns</a></p>
</li>
<li><p><a target="_blank" href="https://refactoring.guru/design-patterns/singleton">Refactor Guru: Singleton</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[NestJS Framework]]></title><description><![CDATA[Pengenalan
Untuk programmer yang sudah menggunakan NodeJS mungkin sudah familiar dengan namanya NestJS Framwork. Ya, NestJS merupakan salah satu framework yang cukup populare di komunitas Javascript dan Typescript. NestJS sebetulnya hanya membungkus ...]]></description><link>https://verri.andriawan.web.id/nestjs-framework</link><guid isPermaLink="true">https://verri.andriawan.web.id/nestjs-framework</guid><category><![CDATA[nestjs]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Wed, 31 Dec 2025 16:59:06 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-pengenalan">Pengenalan</h2>
<p>Untuk programmer yang sudah menggunakan NodeJS mungkin sudah familiar dengan namanya NestJS Framwork. Ya, NestJS merupakan salah satu framework yang cukup populare di komunitas Javascript dan Typescript. NestJS sebetulnya hanya membungkus library-library npm yang populer menjadi framework Layer 2 lalu ditambah aturan dan cara-cara mendefine module agar lebih tertata. Sehingga memaksa programmer untuk mengikuti dan mematuhi arsitektur tertentu dalam pembuatan aplikasinya.</p>
<p>Berdasarkan official site dari NestJS, sudah banyak perusahaan yang menggunakan NestJS sebagai aplikasi enterprise mereka seperti Adidas, Gitlab, Neo4J, BMW, IBM dll. Membuktikan bahwa NestJS sudah cukup mature dan dipercaya oleh perusahaan-perusahaan bersekala local maupun international.</p>
<h2 id="heading-keunggulan">Keunggulan</h2>
<p>NestJS menggunakan pendekatan modularity dan scalability sehingga sangat memudahkan programmer untuk memanage codenya dan mengatur performancenya. Yang mana sebetulnya sama saja seperti menggunakan misal light framework seperti ExpressJS atau Koa. Namun Express atau Koa terlalu liar atau fleksible, sehingga desain architecture yang dibangun setiap programmer akan bervariasi yang akan mengakibatkan learning curve ketika terjadi pemindahan pekerjaan antar programmer cukup sulit.</p>
<p>Keunggulan lainnya dari NestJS yaitu kaya akan fitur-fiturnya dan flexibilitasnya. Seperti:</p>
<ul>
<li><p>Modularity</p>
</li>
<li><p>Scalability</p>
</li>
<li><p>Dependency Injection</p>
</li>
<li><p>Type Safety</p>
</li>
<li><p>Testable</p>
</li>
</ul>
<p>NestJS menggunakan typescript sebagai dasar bahasa pemogramannya sehingga memaksa programmer untuk selalu mengikuti aturan atau design yang sudah dibuat dan disetujui bersama, selain itu karena menggunakan typescript, maka akan lebih aman dimana setiap variable yang dibuat akan didefinisikan tipe datanya secara explisit.</p>
<p>Selain itu, ketika dev menginisiasi module menggunakan <code>Injectable Decorator</code> secara default Nest akan mendeklarasikan module tersebut sebagai Singleton. Dalam beberapa kondisi ini baik karena akan meringankan beban memory. Tapi jika salah menggunakan Pattern ini, bisa berakibat pada masalah <code>Race Condition</code> yang akan saya gambarkan pada article selanjutnya.</p>
<h2 id="heading-memulai-nestjs-project">Memulai NestJS Project</h2>
<p>Untuk bisa menggunakan NestJS, pertama harus menginstall NodeJS terlebih dahulu dengan mengkuti langkah pada official site pada link ini <a target="_blank" href="https://nodejs.org/en/download">(Install NodeJS)</a> atau bisa menggunakan version manager seperti NVM dengan cara mengikuti cara installation-nya pada official dokumentasinya <a target="_blank" href="https://github.com/nvm-sh/nvm?tab=readme-ov-file#installing-and-updating">(Install NVM)</a>.</p>
<p>Untuk memvalidasi bahwa dengan menjalankan</p>
<pre><code class="lang-bash">$ node --version
v22.17.0
</code></pre>
<p>Jika sudah tertampil version dari NodeJS maka NodeJS berhasil di install. Selanjutnya bisa mencoba initial NestJS Project dengan menjalankan command seperti ini.</p>
<pre><code class="lang-bash">$ npm i -g @nestjs/cli
$ nest new coba
</code></pre>
<p>Step selanjutnya akan ada option agar kita memilih akan menggunakan package manager apa, ada <code>npm</code>, <code>yarn</code>, dan <code>pnpm</code>. Secara default nest akan memilih <code>npm</code>. Jadi bisa langsung hit enter saja agar secara default memilih <code>npm</code>.</p>
<pre><code class="lang-typescript">✨  We will scaffold your app <span class="hljs-keyword">in</span> a few seconds..

? Which package manager would you ❤️  to use?
❯ npm
  yarn
  pnpm

✨  We will scaffold your app <span class="hljs-keyword">in</span> a few seconds..

✔ Which package manager would you ❤️  to use? npm
CREATE coba/.prettierrc (<span class="hljs-number">52</span> bytes)
CREATE coba/README.md (<span class="hljs-number">5028</span> bytes)
CREATE coba/eslint.config.mjs (<span class="hljs-number">899</span> bytes)
CREATE coba/nest-cli.json (<span class="hljs-number">171</span> bytes)
CREATE coba/package.json (<span class="hljs-number">1975</span> bytes)
CREATE coba/tsconfig.build.json (<span class="hljs-number">97</span> bytes)
CREATE coba/tsconfig.json (<span class="hljs-number">677</span> bytes)
CREATE coba/src/app.controller.ts (<span class="hljs-number">274</span> bytes)
CREATE coba/src/app.module.ts (<span class="hljs-number">249</span> bytes)
CREATE coba/src/app.service.ts (<span class="hljs-number">142</span> bytes)
CREATE coba/src/main.ts (<span class="hljs-number">228</span> bytes)
CREATE coba/src/app.controller.spec.ts (<span class="hljs-number">617</span> bytes)
CREATE coba/test/jest-e2e.json (<span class="hljs-number">183</span> bytes)
CREATE coba/test/app.e2e-spec.ts (<span class="hljs-number">669</span> bytes)

✔ Installation <span class="hljs-keyword">in</span> progress... ☕

🚀  Successfully created project coba
</code></pre>
<p>Jika sudah tertampil <code>successfully</code> berarti setup projek berhasil dilakukan.</p>
<h2 id="heading-generated-files">Generated files</h2>
<p>Secara default, file yang digenerate oleh NestJS Cli akan terlihat cukup simple. Jika masuk ke directory yang baru digenerate maka akan terlihat file-file ini.</p>
<pre><code class="lang-typescript">coba/src
  - app.module.ts
  - app.service.ts
  - app.controller.ts
  - app.controller.spec.ts
  - main.ts
</code></pre>
<ul>
<li><strong>main.ts</strong></li>
</ul>
<p>file ini akan menjadi entrypoint ketika app dijalankan karena bootstrap dari aplikasi ada pada file ini.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { NestFactory } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/core'</span>;
<span class="hljs-keyword">import</span> { AppModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'./app.module'</span>;

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">bootstrap</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> app = <span class="hljs-keyword">await</span> NestFactory.create(AppModule);
  <span class="hljs-keyword">await</span> app.listen(process.env.PORT ?? <span class="hljs-number">3000</span>);
}
bootstrap();
</code></pre>
<ul>
<li><strong>app.controller.ts</strong></li>
</ul>
<p>File controller adalah merupakan translator router dari setiap endpoint yang akan diakses dari luar layer.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Controller, Get } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>;
<span class="hljs-keyword">import</span> { AppService } <span class="hljs-keyword">from</span> <span class="hljs-string">'./app.service'</span>;

<span class="hljs-meta">@Controller</span>()
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> AppController {
  <span class="hljs-keyword">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> appService: AppService</span>) {}

  <span class="hljs-meta">@Get</span>()
  getHello(): <span class="hljs-built_in">string</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.appService.getHello();
  }
}
</code></pre>
<ul>
<li><strong>app.service.ts</strong></li>
</ul>
<p>File service ini umumnya akan berisi business logic dari aplikasi. Module ini di-inject ke controller sebagai logic utama yg akan eksekusi.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Injectable } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>;

<span class="hljs-meta">@Injectable</span>()
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> AppService {
  getHello(): <span class="hljs-built_in">string</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">'Hello World!'</span>;
  }
}
</code></pre>
<ul>
<li><strong>app.module.ts</strong></li>
</ul>
<p>File ini digunakan untuk mengumpulkan semua module agar bisa saling terhubung antar module-module lain yang terlbat dalam module tersebut.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Module } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>;
<span class="hljs-keyword">import</span> { AppController } <span class="hljs-keyword">from</span> <span class="hljs-string">'./app.controller'</span>;
<span class="hljs-keyword">import</span> { AppService } <span class="hljs-keyword">from</span> <span class="hljs-string">'./app.service'</span>;

<span class="hljs-meta">@Module</span>({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> AppModule {}
</code></pre>
<h2 id="heading-running">Running</h2>
<p>Jika masuk ke folder tersebut melalui terminal dan jalankan command <code>npm run start:dev</code> dimana secara default akan listen di port 3000. Bisa dicoba dengan mengarahkan browser ke alamat <a target="_blank" href="http://localhost:3000"><code>http://localhost:3000</code></a>. Maka akan tertampil <code>Hello world</code> pada browser.</p>
<h2 id="heading-references">References</h2>
<ul>
<li><p><a target="_blank" href="https://docs.nestjs.com/">https://docs.nestjs.com</a></p>
</li>
<li><p><a target="_blank" href="https://nodejs.org/en/download">https://nodejs.org/en/download</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/nvm-sh/nvm">https://github.com/nvm-sh/nvm</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Dependency Inversion Principle (DIP)]]></title><description><![CDATA[Ide utama dari prinsip ini adalah mengurangi masalah saling ketergantungan dari module-module pada software atau aplikasi. Karena ketergantungan antar module, akan menyulitkan dalam memaintain module-module tersebut pada fase selanjutnya. Sebagai con...]]></description><link>https://verri.andriawan.web.id/dependency-inversion-principle-dip</link><guid isPermaLink="true">https://verri.andriawan.web.id/dependency-inversion-principle-dip</guid><category><![CDATA[SOLID principles]]></category><category><![CDATA[dependency inversion]]></category><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Thu, 12 Jun 2025 17:00:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748695292718/0eeb647d-ad60-4aaf-aa09-a31a256299c4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Ide utama dari prinsip ini adalah mengurangi masalah saling ketergantungan dari module-module pada software atau aplikasi. Karena ketergantungan antar module, akan menyulitkan dalam memaintain module-module tersebut pada fase selanjutnya. Sebagai contoh yaitu ketika ada perubahan pada low level module, maka high level module yang terkait dengan module tersebut juga harus mengalami perubahan. Akibatnya jika module-module yang terkait pada module yang berubah tersebut banyak jumlahnya, dipastikan menambah beban <em>timeline development</em> untuk merivisi module-module tersebut lebih panjang. Hal tersebut akan sangat-sangat berpengaruh besar pada biaya development dan bisnis. Selain itu juga akan memperbesar kemungkinan kerusakan pada module-module yang tidak seharusnya ikut diubah.</p>
<blockquote>
<p>Tulisan ini adalah bagian dari <a target="_blank" href="https://verri.andriawan.web.id/series/solid-principles">SOLID Principles</a> Series, sehingga untuk bisa membaca secara menyeluruh dan berkesinambungan, alangkah lebih baik bisa membaca secara urut dari link series <a target="_blank" href="https://verri.andriawan.web.id/series/solid-principles">berikut</a>.</p>
</blockquote>
<p>Berdasarkan Wikipedia, dua point utama dari prinsip ini:</p>
<ul>
<li><p>High level modules should not import anything from low-level modules. Both should depend on abstractions (module-module High-level)</p>
</li>
<li><p>Abstraction should not depend on details. Details (concrete implementations) should depend on abstractions.</p>
</li>
</ul>
<p>Beberapa mungkin bingung, seperti apa itu high-level module, apa itu low-level module dan juga yang dimaksud abstraction. Yang pertama perlu diingat bahwa module itu bisa berupa class, bisa kumpulan class untuk menyelesaikan kasus penggunaan tertentu (usecase). Berikut sedikit penjelasan mengenai istilah-istilah tersebut.</p>
<ul>
<li><p>High-Level module merupakan module teratas/terdepan yang memerlukan module/class yang lebih detail untuk menyelesaikan suatu masalah atau logic. Cirinya, module ini akan menginisialisasi module lain di dalamnya.</p>
</li>
<li><p>Low-Level module merupakan module yang lebih detail untuk menyelesaikan suatu masalah atau logic. Cirinya, module ini tidak menginisialisasi module tertentu di dalamnya.</p>
</li>
<li><p>Abstraction, proses men-generalkan sebuah logic yang kompleks atau menyembunyikan detail-detail yang tidak diperlukan untuk diketahui antar module. Biasanya menggunakan interface atau abstract class.</p>
</li>
</ul>
<p>Untuk lebih memperjelas, mari kita lihat dari contoh code berikut.</p>
<p><strong>Account Class File</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> EmailAccount {
    <span class="hljs-keyword">public</span> emailBucket: <span class="hljs-built_in">string</span>[] = []
    <span class="hljs-keyword">public</span> notifyType: <span class="hljs-built_in">string</span>

    <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
        <span class="hljs-built_in">this</span>.sendType = <span class="hljs-string">'email'</span>
    }

    addEmail(email: <span class="hljs-built_in">string</span>) {
        <span class="hljs-built_in">this</span>.emailBucket.push(email)
    }

    validate(email: <span class="hljs-built_in">string</span>) {
        <span class="hljs-keyword">return</span> email
    }
}
</code></pre>
<p><strong>Notification Class File</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> Notifier {
  <span class="hljs-keyword">private</span> account: EmailAccount

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params">email: <span class="hljs-built_in">string</span></span>) {
     <span class="hljs-built_in">this</span>.account = <span class="hljs-keyword">new</span> EmailAccount
     <span class="hljs-built_in">this</span>.account.addEmail(email)
  }

  send() {
      <span class="hljs-keyword">const</span> data = <span class="hljs-built_in">this</span>.send.emailBucket
      <span class="hljs-keyword">const</span> <span class="hljs-keyword">type</span> = <span class="hljs-built_in">this</span>.send.notifyType

      <span class="hljs-built_in">this</span>.sendTo(<span class="hljs-keyword">type</span>, data)
  }

     sendTo(<span class="hljs-keyword">type</span>: <span class="hljs-built_in">string</span>, data: <span class="hljs-built_in">string</span>) {
         <span class="hljs-built_in">console</span>.log({<span class="hljs-keyword">type</span>, data})
     }
}
</code></pre>
<p>Code di atas memperlihatkan bahwa class <code>Notifier</code> adalah <code>high-level module</code> karena class atau module ini membutuhkan class yang lebih detail (<code>EmailAccount</code>) untuk menyelesaikan tugasnya, terlihat pada constructor class ini. Sedangkan class <code>EmailAccount</code> disebut juga sebagai <code>low-level module</code> karena class atau module ini lebih explicit yaitu management account email dan utamanya module ini tidak menginisialisasi atau tidak memerlukan module lain di dalamnya.</p>
<p>Seperti terlihat pada class <code>Notifier</code> pada section constructor, class ini meng-initialisasi <code>EmailAccount</code> yang diset ke property <code>account</code>. Lalu pada bagian method <code>send</code> terjadi pengambilan property <code>emailBucket</code> dan <code>notifyType</code> yang merupaka property milik <code>EmailAccount</code>. Bagian ini lah yang menjadi masalah. Class atau module ini menyalahi aturan dari Dependency Inversion Principle (DIP). Karena seandainya terjadi perubahan pada property pada module EmailAccount, misalnya property <code>emailBucket</code> berubah menjadi <code>emails</code> maka programmer harus merevisi juga <code>high-level module</code> yaitu class <code>Notifier</code> . Seharusnya agar tidak melanggar aturan DIP, sebaiknya jika terjadi perubahan pada <code>EmailAccount</code> module , maka pada <code>Notifier</code> tidak perlu ada perubahan. Untuk programmer perlu melakukan abstraction yang biasanya menggunakan <code>interface</code> atau <code>abstract class</code> . Sebagai contoh code di sebelumnya diubah menjadi seperti berikut.</p>
<p><strong>Abstraction</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Prop = {
    <span class="hljs-keyword">type</span>: <span class="hljs-built_in">string</span>
    data: <span class="hljs-built_in">string</span>[]

}

<span class="hljs-comment">// MG is short of Management</span>
<span class="hljs-keyword">interface</span> IAccountMG {
    getData(): Prop
}
</code></pre>
<p>Pertama kita perlu melakukan <code>abstraction</code>. Seperti pada contoh snippet code di atas melakukan abstraction dengan memanfaatkan <code>type</code> dan <code>interface</code> . Di typescript sendiri, kita bisa menggunakan banyak cara untuk <code>abstraction</code>, bisa menggunakan <code>type</code>, <code>interface</code> atau juga menggunakan <code>abstract class</code>. Tapi untuk contoh kasus di code ini cukup menggunakan <code>type</code> dan <code>interface</code> saja.</p>
<p>Seperti yang terlihat pada interface <code>IAccountMG</code> terdapat member method <code>getData</code> dengan return value berupa <code>type Prop</code> . Tujuan dari <code>type Prop</code> adalah kontrak agar module apapun yang meng-implement <code>interface IAccountMG</code> maka return dari <code>getData</code> harus selalu ber-type <code>Prop</code> dengan begitu return type akan selalu konsisten.</p>
<p><strong>Account class</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> EmailAccount <span class="hljs-keyword">implements</span> IAccountMG {
    <span class="hljs-keyword">public</span> emailBucket: <span class="hljs-built_in">string</span>[] = []
    <span class="hljs-keyword">public</span> notifyType: <span class="hljs-built_in">string</span>

    <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
        <span class="hljs-built_in">this</span>.sendType = <span class="hljs-string">'email'</span>
    }

    addEmail(email: <span class="hljs-built_in">string</span>) {
        email = <span class="hljs-built_in">this</span>.validate(email)
        <span class="hljs-built_in">this</span>.emailBucket.push(email)
    }

    validate(email: <span class="hljs-built_in">string</span>) {
        <span class="hljs-keyword">return</span> email
    }

    getData() {
        <span class="hljs-keyword">return</span> {
            data: <span class="hljs-built_in">this</span>.emailBucket,
            <span class="hljs-keyword">type</span>: <span class="hljs-built_in">this</span>.notifyType
        }
    }

}
</code></pre>
<p>Selanjutnya class <code>EmailAccount</code> meng-implement interface <code>IAccountMG</code>. Dengan meng-implement interface <code>IAccountMG</code> maka class <code>EmailAccount</code> akan dipaksa untuk meng-implementasi method <code>getData</code> juga. Karena method ini yang akan di call pada <code>high-level module</code> maka dari itu method <code>getData</code> wajib ada dan <em>callable</em> (bisa dieksekusi)<em>.</em></p>
<p><strong>Notifier Class</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> Notifier {
  <span class="hljs-keyword">private</span> account: IAccountMG

  <span class="hljs-keyword">constructor</span>(<span class="hljs-params">email: <span class="hljs-built_in">string</span></span>) {
    <span class="hljs-built_in">this</span>.account = <span class="hljs-keyword">new</span> EmailAccount
    <span class="hljs-built_in">this</span>.account.addEmail(email)
  }

  send() {
    <span class="hljs-keyword">const</span> payload: Prop = <span class="hljs-built_in">this</span>.account.getData()
    <span class="hljs-built_in">this</span>.sendTo(payload)
  }

  sendTo(payload: Prop) {
    <span class="hljs-built_in">console</span>.log(payload)
  }
}
</code></pre>
<p>Lalu pada <code>contructor</code> class <code>Notifier</code> sama seperti sebelumnya yaitu menginisialisasi class EmailAccount. Tapi yang berbeda kali ini adalah type property <code>account</code> pada <code>Notifier</code> bukan lagi <code>EmailAccount</code> melainkan interface <code>IAccountMG</code>. Dengan begini module <code>Notifier</code> tidak perlu tahu yang dilakukan <code>getData</code> pada module <code>account</code>. Yang perlu diketahui oleh module <code>Notifier</code> hanya bahwa <code>getData</code> bersifat dapat dieksekusi dan memberikan return type Prop. Itulah tujuan dari <code>abstraction</code> yaitu menyederhanakan module dengan menyembunyikan detail pada <code>Low-Level Module</code> yang tidak perlu diketahui oleh <code>High-Level module</code>.</p>
<p>Lalu kenapa menggunakan penamaan method <code>getData</code> dari pada <code>getEmail</code> misalnya. Kembali ke point ke dua pada gagasan dari DIP, yaitu abstraction tidak boleh berdasarkan detail dari implementator. Implementator-lah yang harus mengikuti abstraction. Maksudnya penamaan dan fungsi member method harus lebih general, seperti misal pada kasus code di sini, jika misalkan akan menambah module lain yang bukan berupa kumpulan email maka <code>getData</code> sudah cukup untuk merepresentasikan module implementatornya.</p>
<p><strong>references:</strong></p>
<ul>
<li><p><a target="_blank" href="https://www.tutorialsteacher.com/csharp/dependency-inversion-principle">https://www.tutorialsteacher.com/csharp/dependency-inversion-principle</a></p>
</li>
<li><p><a target="_blank" href="https://salesforcecodex.com/architecture/understanding-dependency-inversion-principle-with-c/">https://salesforcecodex.com/architecture/understanding-dependency-inversion-principle-with-c/</a></p>
</li>
<li><p><a target="_blank" href="https://code-maze.com/dependency-inversion-principle/">https://code-maze.com/dependency-inversion-principle/</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Interface Segregation Principle (ISP)]]></title><description><![CDATA[ISP terdiri dari dua kata utama yaitu Interface dan Segregation, yang mana interface pada ISP disebut sebagai kontrak atau protokol. Sedangkan Segregation berarti memisahkan. Dari dua kata ini bisa di]]></description><link>https://verri.andriawan.web.id/interface-segregation-principle-isp</link><guid isPermaLink="true">https://verri.andriawan.web.id/interface-segregation-principle-isp</guid><category><![CDATA[SOLID principles]]></category><category><![CDATA[Interface Segregation ]]></category><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Thu, 05 Jun 2025 17:00:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748695245141/10dccb6a-58ab-4c04-b6a8-2884c3d635bd.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>ISP terdiri dari dua kata utama yaitu Interface dan Segregation, yang mana interface pada ISP disebut sebagai kontrak atau protokol. Sedangkan Segregation berarti memisahkan. Dari dua kata ini bisa diambil kesimpulan bahwa ISP adalah Pemisahan kontrak-kontrak atau protokol-protokol yang terlalu besar menjadi protokol-protokol atau kontrak-kontrak kecil.</p>
<blockquote>
<p>Tulisan ini adalah bagian dari <a href="https://verri.andriawan.web.id/series/solid-principles">SOLID Principles</a> Series, sehingga untuk bisa membaca secara menyeluruh dan berkesinambungan, alangkah lebih baik bisa membaca secara urut dari link series <a href="https://verri.andriawan.web.id/series/solid-principles">berikut</a>.</p>
</blockquote>
<p>Berdasarkan history-nya, ISP dikenalkan ketika Robert C. Martin berkonsultasi dengan Xerox yang merupakan brand dari Mesin Printer. Pada saat itu Xerox membuat printer baru yang mana bisa menambah beberapa fitur yang di versi printer lainnya tidak ada. Pada saat itu, Xerox menambah fitur baru seperti kemampuan Stapling (Menstaples kertas) dan Faxing (Menerima dan mengirim Fax) pada printer barunya. Dengan adanya penanambahan fitur tersebut maka pasti akan diperlukan perubahan program yang tentunnya akan berpengaruh pada program yang digunakan oleh device printer versi lainnya. Jika desain dari program bermasalah, maka potensi akan mengakibatkan error pada device printer lainnya akan tinggi ketika program printer di-update. Maka dari itu munculah gagasan baru yang sekarang kita kenal ISP (<em>Interface Segregation Principle</em>).</p>
<p>Ide dari Interface Segregation Principle cukup sederhana, yaitu</p>
<blockquote>
<p><em>“Client should not be forced to depend on methods it does not use”</em></p>
</blockquote>
<p>Yang berarti Client (class/object yg berkorelasi) seharusnya tidak dipaksa untuk meimplementasikan method yang diluar dari tugas spesifik clientnya atau objectnya. Dengan begitu class atau object akan lebih ringkas karena tidak mengimplementasikan banyak method-method yang tidak digunakan ke dalam class tersebut selain itu class atau object akan hanya memiliki tugas yang spesifik (SRP).</p>
<p>Sebagai contoh kasus, kita gunakan contoh kasus sebelumnya tentang Vending Machine.</p>
<p>Asumsi kita memiliki sebuah class NotifyEvent seperti ini</p>
<pre><code class="language-typescript">class NotifyEvent {
	
	private data: Record&lt;string, any&gt; = {}

	setup(data: Record&lt;string, any&gt;) {
		this.data = data
	}
	
	console() {
	   // Code for testing logging
	}
	
	async send() {
		if (process.env.NODE_ENV === 'testing') {
			return this.console(this.data)
		} else {
			return await this.sendSMS()
		}
	}
	
	async sendSMS() {}
	
}
</code></pre>
<p>Kegunaan utama dari class tersebut adalah untuk mengirim notifikasi SMS yang ditandai dengan adanya method <code>sendSMS</code> pada class tersebut. Sedangkan pada contoh ini, method <code>console</code> digunakan untuk kebutuhan automated test untuk usecase tersebut. Jika mengikuti class tersebut, bentuk interface kurang lebih akan seperti ini:</p>
<pre><code class="language-typescript">interface INotifyEvent {
  setup(data: Record&lt;string, any&gt;): void
  console(): void
  send(): Promise&lt;void&gt;
  sendSMS(): Promise&lt;void&gt;
}
</code></pre>
<p>Pada contoh di atas mungkin interface masih terlihat <em>lean</em> karena method digunakan sedikit, tapi ketika method yang dimiliki class tersebut cukup banyak, maka interface akan menjadi besar (bloat). Sehingga perlu dilakukan penyederhanaan interface dengan cara memisahakan interface berdasarkan fungsi utamanya dengan tujuan agar class yang mengimplementasi interface tersebut tidak dipaksa untuk mengimplementasikan method yang tidak diperlukan.</p>
<p>Maka interface akan menjadi kurang lebih seperti ini:</p>
<pre><code class="language-typescript">interface INotifyEvent {
	setup(data: Record&lt;string, any&gt;): void
	send(): Promise&lt;void&gt;
}

interface INotifySMSEvent extends INotifyEvent {
	sendSMS(): Promise&lt;void&gt;
}

interface INotifyTestEvent extends INotifyEvent {
  console(): void
}
</code></pre>
<p>Dengan memisahkan interface seperti ini, maka class yang mengimplementasikannya tidak akan dipaksa mengimplementasikan method yang tidak diperlukan. Misal jika class akan digunakan untuk keperluan testing, maka class akan mengimplementasi interface <code>INotifyTestEvent</code> yang hanya memerlukan method console. Class yang bersangkutan tidak akan dipaksa mengimplemntasikan method <code>sendSMS</code> karena tidak diperlukan. Sehingga class akan lebih simple dan sesuai kegunaannya.</p>
<p><strong>references:</strong></p>
<ul>
<li><p><a href="https://hackernoon.com/grasp-principles-part-2-controller-low-coupling-and-high-cohesio">https://hackernoon.com/grasp-principles-part-2-controller-low-coupling-and-high-cohesio</a></p>
</li>
<li><p><a href="https://www.infoq.com/minibooks/reexamining-microservices/">https://www.infoq.com/minibooks/reexamining-microservices/</a></p>
</li>
<li><p><a href="https://drive.google.com/file/d/0BwhCYaYDn8EgOTViYjJhYzMtMzYxMC00MzFjLWJjMzYtOGJiMDc5N2JkYmJi/view?resourcekey=0-3H2Ld4l-dIZZVRmHSpNLcA">Robert C Martin, The Interface Segregation Principle, C++ Report, June 1996</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Liskov Subtitution Principle (LSP)]]></title><description><![CDATA[Gagasan atau idea yang dikenalkan oleh Barbara Liskov pada tahun 1987 pada conference dengan judul Data abstraction and Hierarchy. Dalam gagasannya memiliki kesimpulan kurang lebih seperti ini, Jika S adalah subtitusi dari T, maka T bisa digantikan o...]]></description><link>https://verri.andriawan.web.id/liskov-subtitution-principle-lsp</link><guid isPermaLink="true">https://verri.andriawan.web.id/liskov-subtitution-principle-lsp</guid><category><![CDATA[Liskov Substitution Principle]]></category><category><![CDATA[SOLID principles]]></category><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Sat, 31 May 2025 17:00:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748695187709/779ab540-5146-4b2e-abe6-8e4e704a8bd7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Gagasan atau idea yang dikenalkan oleh Barbara Liskov pada tahun 1987 pada conference dengan judul <em>Data abstraction and Hierarchy</em>. Dalam gagasannya memiliki kesimpulan kurang lebih seperti ini, Jika S adalah subtitusi dari T, maka T bisa digantikan oleh S tanpa merubah properti-properti pada program. Maka syarat S agar dapat menggantikan T adalah S harus memiliki perilaku seperti T dengan syarat-syarat tertentu.</p>
<blockquote>
<p>Tulisan ini adalah bagian dari <a target="_blank" href="https://verri.andriawan.web.id/series/solid-principles">SOLID Principles</a> Series, sehingga untuk bisa membaca secara menyeluruh dan berkesinambungan, alangkah lebih baik bisa membaca secara urut dari link series <a target="_blank" href="https://verri.andriawan.web.id/series/solid-principles">berikut</a>.</p>
</blockquote>
<p>Jika dalam buku <em>“Design Principles and Design Pattern”</em> menyebutkan seperti ini</p>
<blockquote>
<p><em>“Subclass should be substitutable for their base classes”</em></p>
</blockquote>
<p>Bahwa class turunan harus bisa disubtitusikan untuk Class Basenya. Sebagai contoh, jika Sebuah function semisal <code>function office(ketua: PemimpinClass)</code>, jika <code>WakilKetuaClass</code> merupakan subclass dari <code>PemimpinClass</code> maka argument pada fungsi <code>office</code> bisa digantikan oleh <code>WakilKetuaClass</code>.</p>
<p>Principle ini terlihat sederhana dan banyak yang tidak terlalu mendetailkan prinsip ini. Menurut saya pribadi prinsip ini merupakan prinsip paling rumit dibanding 4 prinsip lainnya dalam SOLID, karena pada prinsip ini ada beberapa aturan yang harus dipatuhi. Terutama konsep-konsep dasar pada OOP seperti <em>Covariance</em>, <em>Contravariance</em> dan <em>Invariant</em>. Jika belum memahami tentang hal tersebut silahkan membaca posting sebelumnya di link berikut.</p>
<blockquote>
<p><a target="_blank" href="https://verri.andriawan.web.id/covariance-dan-contravariance">Covariance dan Contravariane</a></p>
</blockquote>
<p>Selain itu silahkan membaca posting lainnya tentang class invariant pada link <a target="_blank" href="https://hashnode.com/post/cmasacigd000d09jv9w6tdbxf">berikut</a>.</p>
<blockquote>
<p><a target="_blank" href="https://hashnode.com/post/cmasacigd000d09jv9w6tdbxf">Class Invariant</a></p>
</blockquote>
<p>Perlunya memahami konsep-konsep dasar tersebut dapat mempermudah reader dalam memahami aturan-aturan yang perlu dipatuhi pada LSP. Sederhananya pada prinsip ini adalah tentang mensubtitusikan/menggantikan Obejct sejenis. Sebagai contoh.</p>
<pre><code class="lang-tsx">// super type
class Machine {}

// sub type mini machine
class MiniMachine extends Machine {}

// sub type large machine
class LargeMachine extends Machine {}
</code></pre>
<p>Jika melihat cuplikan code tersebut akan mengerti apa yang dimaksud mensubtitusi. Dimana pada cuplikan code tersebut terdapat tiga buah class, dimana dua diantaranya adalah subtype. Jika kedua subtype tersebut diimplementasikan akan seperti ini</p>
<pre><code class="lang-tsx">const mini = new MiniMachine
const large = new LargeMachine

function dispatch(machine: Machine) {}
</code></pre>
<p>Pada function <code>dispatch</code> terdapat param <code>machine</code> yang bertype <code>Machine</code>. Karena param machine bertipe <code>Machine</code> maka dua instance yang dibuat bisa saling menggantikan sebagai argument pada param machine di fungsi <code>dispatch</code> karena kedua instance tersebut merupakan turunan dari class Machine.</p>
<p>Namun LSP tidak sesederhana itu. Ada aturan-aturan yang perlu dipatuhi. Hal tersebut diperlukan guna mejaga ketahanan dari program yang dibuat.</p>
<h2 id="heading-aturan-aturan-lsp">Aturan-aturan LSP</h2>
<p>Berikut ini adalah pejelasan tentang aturan-aturan yang perlu dipatuhi.</p>
<h3 id="heading-signature-rules">Signature Rules</h3>
<ul>
<li><p><strong>Convariance</strong> dimana keluar dari method atau function harus memiliki degree yang lebih kecil dari suppertype-nya.</p>
</li>
<li><p><strong>Contravariance</strong> dimana nilai argument harus memiliki degree lebih besar dari pada supertype-nya.</p>
</li>
<li><p>Jumlah <code>throw exception</code> pada logika method bisa dikurangi, tapi tidak boleh ditambah.</p>
</li>
</ul>
<p>Untuk pembahasan tentang <a target="_blank" href="https://verri.andriawan.web.id/covariance-dan-contravariance">Covariance dan Contravariance</a> bisa membaca artikle tentang ini terlebih dahulu pada link <a target="_blank" href="https://verri.andriawan.web.id/covariance-dan-contravariance">berikut</a>. Aturan lainnya bahwa jumlah <strong>Throw Exception</strong> pada method <strong>boleh dikurangi</strong> tapi <strong>tidak boleh ditambah</strong>. Karena akan mengakibatkan unpredictable throw exception yang mengakibatkan system break.</p>
<h3 id="heading-property-rules">Property Rules</h3>
<ul>
<li><strong>Class invariant</strong></li>
</ul>
<p>Dimana class/object selalu terjaga dan tergaransi selama object class tersebut hidup. Untuk penjelasan detail tentang class invariant bisa membaca pada post sebelumnya di link berikut</p>
<blockquote>
<p><a target="_blank" href="https://hashnode.com/post/cmasacigd000d09jv9w6tdbxf">Class Invariant</a></p>
</blockquote>
<ul>
<li><strong>History constraint</strong></li>
</ul>
<p>Dimana property yang seharusnya immutable tetap terjaga atau tidak boleh berubah. Sebagai contoh.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> VendingMachine {
    <span class="hljs-keyword">protected</span> maxItem: <span class="hljs-built_in">number</span> = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">constructor</span>(<span class="hljs-params">maxItem: <span class="hljs-built_in">number</span></span>) {
       <span class="hljs-built_in">this</span>.maxItem = maxItem;
    }
}
</code></pre>
<p>Pada class <code>VendingMachine</code> terdapat property <code>maxItem</code> pada code di atas. Property ini hanya boleh di-set nilai sekali ketika pembuatan object. Karena pada dasarnya <code>maxItem</code> adalah asumsi bahwa ketika pembuatan machine, maksimal barang yang bisa ditampung sesuai dengan desain dari mechine itu sendiri dan bernilai constant selama mesin berjalan. Sehingga kecil kemungkinan jika ada perubahan nilai <code>maxItem</code> tersebut.</p>
<ul>
<li><strong>Preconditions boleh dipertahankan tapi tidak boleh dikuatkan dari method di class parent-nya.</strong></li>
</ul>
<p>Dimana terdapat conditional flow yang mungkin ada pada head dari sebuah method public pada supertype, atau bisa kita syarat yang harus dipenuhi sebelum masuk ke section logic dari method tersebut. Misal seperti code di bawah ini.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> VendingMachine {    
    <span class="hljs-keyword">protected</span> maxItem: <span class="hljs-built_in">number</span> = <span class="hljs-number">10</span>;

    doSomething(min: <span class="hljs-built_in">number</span>) {

      <span class="hljs-comment">// pre condition</span>
      <span class="hljs-keyword">if</span> (min &lt; <span class="hljs-number">2</span> || min &gt;= <span class="hljs-built_in">this</span>.maxItem) {
         <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Invalid min item"</span>)
      }

      <span class="hljs-comment">// main logic method</span>

      <span class="hljs-comment">// post condition</span>
    }
}

<span class="hljs-keyword">class</span> MiniMachine <span class="hljs-keyword">extends</span> VendingMachine {

    doSomething(min: <span class="hljs-built_in">number</span>) {
        <span class="hljs-comment">// dikuatkan, dilarang menguatkan</span>
        <span class="hljs-comment">// ---</span>
        <span class="hljs-comment">// if (min &lt; 3 || min &gt;= this.maxItem) {</span>
        <span class="hljs-comment">//   throw new Error("Invalid min item")</span>
        <span class="hljs-comment">// }</span>

        <span class="hljs-comment">// dilemahkan, diperbolehkan</span>
        <span class="hljs-keyword">if</span> (min &lt; <span class="hljs-number">1</span> || min &gt;= <span class="hljs-built_in">this</span>.maxItem) {
         <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Invalid min item"</span>)
      }
    }

}
</code></pre>
<p>Pada cuplikan code tersebut, pada method <code>doSomething</code> terdapat control flow dimana nilai <code>min</code> tidak boleh kurang dari <code>2</code>. Maka pada method di subtype-nya, nilai <code>2</code> tersebut tidak boleh dikuatkan misalkan menjadi <code>3</code>. Hal tersebut bisa mengakibatkan program break/berhenti jika terdapat program lain menggunakan/mengeksekusi method tersebut. Kecuali jika batas tersebut dilemahkan, misal jadi batasnya 1 item. Hal tersebut tidak akan bermasalah, karena secara asumsinya ketika pertama kali class tersebut didesain, batasan minimal 2 item. Jadi semua client yang menggunkan class tersebut masih mempertahankan bahwa syarat vending machine minimal 2 item. Ketika nilai diganti jadi 1, maka program tidak akan break karena masih memenuhi syarat.</p>
<p>Jadi yang dimaksud pada pre-condition adalah, jangan membuat syarat baru pada head method yang akan membuat program break.</p>
<ul>
<li><strong>Postconditions boleh dipertahankan tapi tidak boleh dilemahkan dari method di class parent-nya</strong></li>
</ul>
<p>Post condition kebalikan dari pre condition. Post condition ini berada pada foot method atau setelah logic utama dari method dijalankan. Dimana syarat dari LSP adalah tidak boleh dilemahkan.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> VendingMachine {    
    <span class="hljs-keyword">protected</span> maxItem: <span class="hljs-built_in">number</span> = <span class="hljs-number">10</span>;

    doSomething(min: <span class="hljs-built_in">number</span>) {

      <span class="hljs-comment">// pre condition</span>

      <span class="hljs-comment">// main logic method</span>
      <span class="hljs-keyword">const</span> sum = min - <span class="hljs-number">3</span>;

      <span class="hljs-comment">// post condition</span>
      <span class="hljs-keyword">if</span> (sum &lt; <span class="hljs-number">5</span>) {
         <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Value is too small"</span>)
      }
    }
}

<span class="hljs-keyword">class</span> MiniMachine <span class="hljs-keyword">extends</span> VendingMachine {
   doSomething(min: <span class="hljs-built_in">number</span>) {

       <span class="hljs-comment">// Dilemahkan, tidak diperbolehkan</span>
       <span class="hljs-comment">// ---</span>
       <span class="hljs-comment">// if (sum &lt; 6) {</span>
       <span class="hljs-comment">//  throw new Error("Invalid result")</span>
       <span class="hljs-comment">// }</span>

       <span class="hljs-comment">// Dikuatkan, diperbolehkan</span>
       <span class="hljs-comment">// --</span>

       <span class="hljs-keyword">if</span> (sum &lt; <span class="hljs-number">3</span>) {
           <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Value is too small"</span>)
       }
   }
}
</code></pre>
<p>Pada cuplikan code tersebut terlihat bahwa jika hasil sum kurang dari <code>5</code> maka <code>tampilkan Error</code>. Ketika sudah banyak client yang menjadi turunan dari class <code>VendingMachine</code> tersebut, berarti sudah mendefinisikan bahwa paling besar adalah nilai <code>5</code> sebagai batas. Jika postcondition pada subtype dilemahkan, misal menjadi <code>sum &lt; 6</code>, maka ketika hasil perhitungan pada main logic bernilai <code>5</code>, program yang seharusnya bisa berlanjut tapi berakhir break karena tidak memenuhi syarat.</p>
]]></content:encoded></item><item><title><![CDATA[Class Invariant]]></title><description><![CDATA[Istilah invarian diambil dari istilah matematika dimana istilah tersebut memiliki definisi sebuah property pada object matematika yang tidak terjadi perubahan setelah melakukan sebuah operasi atau transformasi pada sebuah object matematik. Sedangkan ...]]></description><link>https://verri.andriawan.web.id/class-invariant</link><guid isPermaLink="true">https://verri.andriawan.web.id/class-invariant</guid><category><![CDATA[oop]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Sat, 17 May 2025 13:51:06 GMT</pubDate><content:encoded><![CDATA[<p>Istilah invarian diambil dari istilah matematika dimana istilah tersebut memiliki definisi sebuah property pada object matematika yang tidak terjadi perubahan setelah melakukan sebuah operasi atau transformasi pada sebuah object matematik. Sedangkan pada OOP dikenal sebagai Class Invariant yang memiliki definisi object memiliki kondisi dan property-property pada object selalu valid/benar sehingga object tersebut selalu terjaga selama masa hidup object tersebut.</p>
<blockquote>
<p>In mathematics, an <strong>invariant</strong> is a property of a mathematical object (or a class of mathematical objects) which remains unchanged after operations or transformations of a certain type are applied to the objects.</p>
</blockquote>
<p>Tujuan dari class tersebut untuk menjamin konsistensi data. Selain itu pada dasarnya, class invariant adalah kumpulan asumsi yang divalidasi, dengan begitu bisa mengurangi error pada program dan akan mempermudah proses debuging ketika terjadi masalah.</p>
<h3 id="heading-karakteristik-class-invariant">Karakteristik class invariant</h3>
<p>Syarat-syarat agar object invariant selalu dipertahankan yaitu:</p>
<p><strong>Harus terpenuhi setelah kontruktor dijalankan</strong></p>
<p>Ketika object akan dibuat dari class invariant, pada bagian konstruktor akan melakukan validasi pada data-data yang masuk yang harus bernilai valid sesuai dengan asumsinya. Untuk contoh kasus Vending Machine, bisa kita asumsikan seperti ini.</p>
<ul>
<li><p>Karena keterbatasan space machine, maka maximal total barang yang bisa masuk hanya 10 barang.</p>
</li>
<li><p>Machine boleh dalam kedaan kosong</p>
</li>
</ul>
<p>Jika diinterpretasikan ke dalam bentuk code, maka kurang lebih seperti ini</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> VendingMachine {
    <span class="hljs-keyword">private</span> maxItems: <span class="hljs-built_in">number</span> = <span class="hljs-number">10</span>;
    <span class="hljs-keyword">private</span> totalItem: <span class="hljs-built_in">number</span> = <span class="hljs-number">0</span>;

    <span class="hljs-keyword">constructor</span>(<span class="hljs-params">totalItem: <span class="hljs-built_in">number</span></span>): void {
        <span class="hljs-built_in">this</span>.isInvariant(totalItem)
        <span class="hljs-built_in">this</span>.totalItem = totalItem;
    }

    isInvariant(totalItem: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">boolean</span> {
        <span class="hljs-keyword">if</span> (totalItem &gt; <span class="hljs-built_in">this</span>.maxItems || totalItem &lt; <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Invalid total items"</span>)
        }        
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
    }

}
</code></pre>
<p>Seperti terlihat pada kontruktor class VendingMachine tersebut melakukan validasi untuk totalItem yang mana dengan asumsi bahwa seharusnya totalItem pada VendingMachine tidak lebih dari 10 item karena batas maximal space pada VendingMachine dan juga nilai total Item tidak mungkin kurang dari 0.</p>
<p><strong>Harus dipertahankan oleh semua method public</strong></p>
<p>Ketika ada method public yang bisa berakibat merubah property pada object, maka setiap invariant property harus divalidasi. Sebagi contoh asumsi</p>
<ul>
<li><p>Ketika ada penambahan barang, maka jumlah maksimal barang tidak boleh melebihi 10 barang sesuai kapasitasnya dan juga total item tidak bernilai decimal.</p>
</li>
<li><p>Ketika ada pembelian barang, maka jumlah minimal barang tidak mungkin kurang dari 0 dan tidak mungkin dalam bentuk decimal.</p>
</li>
</ul>
<p>Ketika asumsi tersebut ditranslate ke dalam bentuk code, akan seperti ini:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Item = {
  id: <span class="hljs-built_in">number</span>;
  name: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">class</span> VendingMachine {
    <span class="hljs-keyword">private</span> maxItems: <span class="hljs-built_in">number</span> = <span class="hljs-number">10</span>
    <span class="hljs-keyword">private</span> totalItem: <span class="hljs-built_in">number</span> = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">private</span> database: Record&lt;<span class="hljs-built_in">string</span>, <span class="hljs-built_in">string</span>&gt;;

    <span class="hljs-keyword">constructor</span>(<span class="hljs-params">totalItem: <span class="hljs-built_in">number</span></span>): void {
        <span class="hljs-built_in">this</span>.database = {}
        <span class="hljs-built_in">this</span>.isInvariant(totalItem)
        <span class="hljs-built_in">this</span>.totalItem = totalItem;
    }

    isInvariant(totalItem: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">boolean</span> {
        <span class="hljs-keyword">if</span> (totalItem &gt; <span class="hljs-built_in">this</span>.maxItems || totalItem &lt; <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Invalid total items"</span>)
        }

        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
    }

    addItem(item: Item) {
        <span class="hljs-keyword">if</span> (!item.id) {
          <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Invalid ID"</span>)
        }

        <span class="hljs-built_in">this</span>.database[item.id] = item.name
        <span class="hljs-keyword">let</span> total: <span class="hljs-built_in">number</span> = <span class="hljs-built_in">this</span>.totalItem
        total += <span class="hljs-number">1</span>
        <span class="hljs-built_in">this</span>.isInvariant(total)

        <span class="hljs-built_in">this</span>.totalItem = total
    }

    delItem(id: <span class="hljs-built_in">number</span>) {
        <span class="hljs-keyword">delete</span> <span class="hljs-built_in">this</span>.database[id]
        <span class="hljs-keyword">let</span> total = <span class="hljs-built_in">this</span>.totalItem
        total -= <span class="hljs-number">1</span>
        <span class="hljs-built_in">this</span>.isInvariant(total)

        <span class="hljs-built_in">this</span>.totalItem = total
    }
}
</code></pre>
<p>Pada contoh ini, database yg digunakan adalah Map sederhana tapi pada <em>real world case</em> seharusnya property database adalah instance data storage pada infrastruktur layer jika mengambil istilah dari <em>clean architecture</em>.</p>
<p>Seperti terlihat pada code tersebut bahwa terdapat dua method public dengan nama <code>addItem</code> dan <code>delItem</code> dengan asumsi bahwa <code>addItem</code> digunakan ketika operator Vending Machine menambah item baru dan <code>delItem</code> digunakan ketika ada pembeli yang melakukan penarikan item dari Vending Machine.</p>
<p>Pada setiap kali method-method public dieksekusi, selalu diikuti oleh pemanggilan method <code>isInvariant</code> yang bertujuan untuk menjaga object tetap bersifat invariant.</p>
<p><strong>Selalu digaransi invarian selama object masih ada dalam masa hidupnya</strong></p>
<p>Yang dimaksud pada point ini adalah object invariant akan selalu tergaransi selama object belum dihancurkan (destruction).</p>
<h3 id="heading-kesimpulan">Kesimpulan</h3>
<p>Class invariant adalah konsep yang cukup dasar pada promgraman OOP karena dengan menggunakan konsep ini akan sangat berpengaruh pada ke stabilan dan ketahanan software yang dibuat.</p>
]]></content:encoded></item><item><title><![CDATA[Rust + Sqlx + Sqlite]]></title><description><![CDATA[Untuk sekedar ingin membuat aplikasi sederhana, mungkin tidak memerlukan database server seperti PostgreSQL atau Mysql. Kita bisa memilih untuk menggunakan database sederhana seperti SQLite Database y]]></description><link>https://verri.andriawan.web.id/rust-sqlx-sqlite</link><guid isPermaLink="true">https://verri.andriawan.web.id/rust-sqlx-sqlite</guid><category><![CDATA[Rust]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[SQLite]]></category><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Fri, 28 Feb 2025 17:00:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748757762104/06431588-4322-4dca-9c96-bec1ab6c6981.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Untuk sekedar ingin membuat aplikasi sederhana, mungkin tidak memerlukan database server seperti PostgreSQL atau Mysql. Kita bisa memilih untuk menggunakan database sederhana seperti SQLite Database yang mana outputnya berupa file. Jadi kita bisa mengikutkan file tersebut ke dalam Git Repository sebagai bagian dari code.</p>
<h2>Setup Workspace</h2>
<p>Di ekosistem Rust, untuk menginisialisasi directory workspace bisa menggunakan popular package manager yaitu menggunakan <code>Cargo</code>. Pada tulisan ini saya menggunakan <code>Cargo</code> dan <code>rustc</code> version <code>1.84.1</code></p>
<pre><code class="language-tsx">$ cargo --version
cargo 1.84.1 (66221abde 2024-11-19)

$ rustc --version
rustc 1.84.1 (e71f9a9a9 2025-01-27)
</code></pre>
<p>Selanjutnya dengan membuat workspace baru dengan nama <code>dbconn</code> sebagai contoh</p>
<pre><code class="language-tsx">$ cargo new dbconn
$ cd dbconn
</code></pre>
<p>Agar code bisa terkoneksi ke sqlite, maka diperlukan <code>sqlx</code>. Jalankan command berikut untuk menambahkan <code>sqlx</code>. Pada saat tulisan ini dibuat, <code>sqlx</code> terbaru menggunakan version <code>v0.8.3</code></p>
<pre><code class="language-tsx">$ cargo add sqlx@0.8.3 --features runtime-tokio,sqlite
</code></pre>
<p>Pada command tersebut saya menambahkan option <code>--features</code>. Option ini digunakan agar cargo hanya meng-<em>compile</em> fitur-fitur yang diperlukan saja, sehingga binary yang dihasilkan akan lebih kecil dari pada meng-compile keseluruhan fitur yang mana tidak digunakan. Jika kita buka file <code>Cargo.toml</code> maka kurang lebih flag akan terlihat seperti ini</p>
<pre><code class="language-tsx">[dependencies]
sqlx = { version = "0.8.3", features = ["runtime-tokio", "sqlite"] }
</code></pre>
<p>Selanjutnya karena async pada Rust bisa menggunakan beberapa package library, seperti <code>tokio</code> atau <code>async-std</code>. Pada tulisan ini, akan menggunakan <code>tokio</code> karena pada setup sqlx kita sudah menambahkan fitur yang di-enable adalah <code>runtime-tokio</code>.</p>
<pre><code class="language-bash">$ cargo add tokio@1 --features full
</code></pre>
<p>Setelah semua package yang diperlukan ter-<em>download,</em> selanjutnya jalan <code>cargo check</code> guna memastikan semua dependencies terdownload.</p>
<pre><code class="language-bash">$ cargo check
</code></pre>
<h2>Setup SQLite Database</h2>
<p>Sebelum melakukan perubahan code pada <a href="http://main.rs"><code>main.rs</code></a>, kita perlu setup database sqlite terlebih dahulu. Karena kita belum menggunakan migration tool apapun untuk meng-inisialisasi database, maka perlu melakukannya secara manual dengan langkah seperti berikut.</p>
<pre><code class="language-bash">$ sqlite3 db.sql
sqlite&gt; create table users (id int primary key, name varchar)
sqlite&gt; insert into users (id, name) values (1, 'Sample User')
sqlite&gt; select * from users;
1|Sample User
</code></pre>
<h2>Setup Connection and Query</h2>
<p>Setelah selesai membuat sqlite database. Kita coba kembali ke directory <code>/tmp/dbconn</code>. Dimana pada directory ini terdapat file <a href="http://main.rs"><code>src/main.rs</code></a> yang berisi code start started Rust seperti ini.</p>
<pre><code class="language-rust">fn main() {
   println!("Hello, world!")
}
</code></pre>
<p>Pada step ini kita perlu merevisi code tersebut agar dapat membuka koneksi ke database yang sudah dibuat sebelumnya. Seperti code di bawah ini.</p>
<pre><code class="language-rust">use sqlx::sqlite::SqlitePoolOptions;

#[tokio::main]
async fn main() -&gt; Result&lt;(), sqlx::Error&gt; {
    let mut pool = SqlitePoolOptions::new()
        .max_connections(5)
        .connect("sqlite:db.sql")
        .await?;

    Ok(())
}
</code></pre>
<p>Pada code tersebut terdapat sebuah variable dengan nama <code>pool</code>, dimana variable ini menyimpan instance dari <code>SqlitePoolOptions</code>. Pool ini merupakan kumpulan koneksi yang masih terhubung ke database yang mana memiliki maximal 5 koneksi. Selain itu untuk dapat melakukan query ke database, diperlukan <code>async/await</code>. Maka perlu menambahkan macro <code>tokio::main</code> pada function main.</p>
<p>Selanjutnya perlu membuat struct yang nantinya digunakan sebagai result dari <code>query_as</code> dari <code>sqlx</code>.</p>
<pre><code class="language-rust">use sqlx::{ prelude::FromRow, sqlite::SqlitePoolOptions};

#[derive(Debug, FromRow)]
struct User {
   id: i64,
   name: String
}
</code></pre>
<p>Gunakan struct tersebut sebagai type annotation <code>Vec&lt;User&gt;</code> pada result query, sehingga hasil dari query akan di-<em>casting</em> ke dalam bentuk Struct tersebut seperti ini</p>
<pre><code class="language-rust">async fn main() -&gt; Result&lt;(), sqlx::Error&gt; {
	...
	let result = query_as::&lt;_, User&gt;(r#"select * from users"#)
		.fetch_all(&amp;pool)
		.await?;
	...
}
</code></pre>
<p>Maka secara keseluruhan function main akan seperti berikut</p>
<pre><code class="language-rust">use sqlx::{ prelude::FromRow, sqlite::SqlitePoolOptions, query_as };

#[derive(Debug, FromRow)]
struct User {
    id: i64,
    name: String
}

#[tokio::main]
async fn main() -&gt; Result&lt;(), sqlx::Error&gt; {
    let mut pool = SqlitePoolOptions::new()
        .max_connections(5)
        .connect("sqlite:db.sql")
        .await?;

    let result = query_as::&lt;_, User&gt;(r#"select * from users"#).fetch_all(&amp;pool).await?;
    
    for item in result.iter() {
        println!("id: {}, name: {}", item.id, item.name);
    }

    Ok(())
}
</code></pre>
<p>Setelah semua tersetup, selanjutnya menjalankan <code>cargo run</code> untuk mendapatkan result.</p>
<pre><code class="language-bash">$ cargo run
...
Compiling dbconn v0.1.0 (/tmp/dbconn)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.28s
     Running `target/debug/dbconn`
id: 1, name: Sample User
</code></pre>
<h3>Kesimpulan</h3>
<p>Untuk membuka koneksi dari Rust ke SQLite database bisa dengan mudah menggunakan package <code>sqlx</code>.</p>
]]></content:encoded></item><item><title><![CDATA[Covariance dan Contravariance]]></title><description><![CDATA[Sebagai developer sangat penting untuk bisa memahami konsep Covariance dan Contravariance pada konsep Polymorphism dan inheritance/turunan. Karena pada konsep ini terdapat aturan-aturan yang harus diikuti ketika diperlukan kemanan dari tipe-tipe retu...]]></description><link>https://verri.andriawan.web.id/covariance-dan-contravariance</link><guid isPermaLink="true">https://verri.andriawan.web.id/covariance-dan-contravariance</guid><category><![CDATA[principles]]></category><category><![CDATA[Programming Tips]]></category><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Thu, 20 Feb 2025 17:00:16 GMT</pubDate><content:encoded><![CDATA[<p>Sebagai developer sangat penting untuk bisa memahami konsep <strong>Covariance dan Contravariance</strong> pada konsep Polymorphism dan inheritance/turunan. Karena pada konsep ini terdapat aturan-aturan yang harus diikuti ketika diperlukan kemanan dari tipe-tipe return atau argument.</p>
<p>Coba perhatikan gambar di bawah ini.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739802263616/45fcc509-26a4-476b-b0c5-b4f799d37d5e.png" alt class="image--center mx-auto" /></p>
<p>Gambar 1 diambil dari <a target="_blank" href="https://en.wikipedia.org/wiki/Covariance_and_contravariance_\(computer_science\)">Wiki</a></p>
<h2 id="heading-invariant-kiri">Invariant ( Kiri )</h2>
<p>Pada <em>gambar 1</em> terlihat pada sisi <strong>Kiri.</strong> Aturan yang harus dipatuhi pada subtype-nya (ClassB) tidak ada perubahan signature method baik Nilai argument atau nilai return dari method. Jadi masih mengikuti supertype-nya. Tapi mungkin ada perbedaan pada detail-detail logic method lainnya dengan syarat semua property dan methodnya selalu bernilai valid sesuai asumsi yang sudah dirancang. Untuk pembahasan lebih detail bisa melihat tulisan pada link <a target="_blank" href="https://hashnode.com/post/cmasacigd000d09jv9w6tdbxf">berikut ini.</a></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> KetuaKelas {
    <span class="hljs-keyword">async</span> order(): <span class="hljs-built_in">Promise</span>&lt;OrderResponse&gt; {
        <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.executeOrder()
        <span class="hljs-keyword">return</span> result <span class="hljs-keyword">as</span> OrderResponse
    }

    <span class="hljs-keyword">async</span> executeOrder() {
       <span class="hljs-comment">// Execute </span>
    }
}

<span class="hljs-keyword">class</span> WakilKetua <span class="hljs-keyword">extends</span> KetuaKelas {
   <span class="hljs-keyword">async</span> order(): <span class="hljs-built_in">Promise</span>&lt;OrderResponse&gt; {
      <span class="hljs-keyword">const</span> granted = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.askPermissionKetua()
      <span class="hljs-keyword">if</span> (!granted) {
           <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Unapproved"</span>)
      }

      <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.executeOrder()
      <span class="hljs-keyword">return</span> result <span class="hljs-keyword">as</span> OrderResponse
   }

   <span class="hljs-keyword">async</span> executeOrder() {
       <span class="hljs-comment">// Execute </span>
   }

   <span class="hljs-keyword">async</span> askPermissionKetua() {
      <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
   }
}
</code></pre>
<p>Pada code terdapat dua class yaitu <code>KetuaKelas</code> dan <code>WakilKetua</code> dimana <code>KetuaKelas</code> merupakan <strong>Supertype</strong> dan <code>WakilKetua</code> adalah <strong>Subtype</strong> terlihat dari extends keyword pada class <code>WakilKetua</code>.</p>
<p>Pada class <code>KetuaKelas</code> terdapat method dengan nama <code>order</code> dan juga <code>executeOrder</code>. Dimana method ini juga digunakan pada class <code>WakilKetua</code>. Juga baik di class <code>KetuaKelas</code> dan juga <code>WakilKetua</code> tidak ada perubahan argument maupun return method. Inilah yang disebut <strong>Invariant</strong>. Method di <strong>Subtype</strong> tidak merubah signature dari method di <strong>Supertype</strong>-nya.</p>
<h2 id="heading-covariance-tengah">Covariance ( Tengah )</h2>
<p>Pada gambar 1 bagian <strong>Tengah</strong> disebut covariance, karena return type pada subtype tidak memiliki nilai yang lebih general atau lebih luas dari pada supertype nya. Aturan ini digunakan untuk menjaga output type pada suatu process. Sebagai contoh</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> keranjang: Buah[] = []

<span class="hljs-keyword">class</span> Jambu implement Buah {}

<span class="hljs-keyword">let</span> obj = Jambu
</code></pre>
<p>Pada contoh code tersebut, <code>let keranjang</code> dengan tipe data array dimana isinya adalah kumpulan buah, oleh karena itu pendefinisian pada variable ini adalah <code>Buah[]</code>. <code>Buah</code> disini adalah supertypenya. Ketika <code>obj</code> dari <code>Jambu</code> akan dimasukan ke dalam <code>kerajang</code>. Dimana <code>Jambu</code> adalah bagian dari <code>Buah</code>, maka <code>Jambu</code> boleh dimasukan ke dalam <code>Keranjang</code>. Karena <code>Jambu</code> bagian dari <code>Buah</code> maka Jambu merupakan subtype dari <code>Buah</code>.</p>
<p>Lalu bagaimana dalam turunan/inheritance? sebagai contoh terdapat code seperti ini</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Buah = {
   berbiji: <span class="hljs-built_in">boolean</span>
}
<span class="hljs-keyword">type</span> Jeruk = Buah &amp; { berbiji: <span class="hljs-built_in">boolean</span> }

<span class="hljs-keyword">class</span> Keranjang {
  sudahMatang(): Buah {}
}

<span class="hljs-keyword">class</span> Bungkus <span class="hljs-keyword">extends</span> Keranjang {
   sudahMatang(): Jeruk {}
}
</code></pre>
<p>Pada code tersebut terdapat dua type yaitu <code>Buah</code> dan <code>Jeruk</code> dimana <code>Buah</code> digunakan sebagai <em>return type method</em> <code>sudahMatang</code> pada class <code>Keranjang</code>, sedangkan <code>Jeruk</code> digunakan sebagai <em>return type method</em> <code>sudahMatang</code> pada class <code>Bungkus</code>. Dimana <code>Bungkus</code> adalah turunan dari <code>Keranjang</code>.</p>
<p>Seperti yang kita lihat pada code bahwa pada class <code>Bungkus</code> memiliki method yang sama dengan *parent class-nya, yaitu <code>sudahMatang</code>. Tapi yang membedakan adalah <em>return type</em> pada masing-masing class.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739932842216/a8af62db-c4d6-4a09-a017-2a6358c12984.png" alt class="image--center mx-auto" /></p>
<p>Terlihat bahwa <em>return method</em> pada class <code>Bungkus</code> memiliki nilai lebih spesifik (T’) dari pada <em>return type</em> <em>method</em> (T) pada parent classnya yaitu <code>Keranjang</code>. Maka dari itu dilihat dari arah panah <em>inheritance</em> dan juga <em>degree</em> dari return type searah. Sehingga disebut <em>covariance</em> dengan tujuannya adalah untuk mengamankan <em>return type.</em></p>
<h2 id="heading-contravariance-kanan">Contravariance (Kanan)</h2>
<p>Sisi <strong>Kanan</strong> pada <em>gambar 1</em>**,** disebut <em>Contravariance</em>, dimana nilai Argument yang diikutkan dalam method dari <em>subtype</em> (T) memiliki nilai lebih general atau luas dari pada <em>supertype</em>-nya (T’), disebut sebagai Contravariance. Aturan ini digunakan untuk menjaga type dari argument yang di-<em>override</em> oleh subtypenya.</p>
<p>Sebagai contoh kita bisa lihat code berikut</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Buah = {
   berbiji: <span class="hljs-built_in">boolean</span>
}
<span class="hljs-keyword">type</span> Jeruk = Buah &amp; { berbiji: <span class="hljs-built_in">boolean</span> }

<span class="hljs-keyword">class</span> Keranjang {
  sudahMatang(item: Jeruk){} 
}

<span class="hljs-keyword">class</span> Bungkus <span class="hljs-keyword">extends</span> Keranjang {
   sudahMatang(item: Buah) {}
}
</code></pre>
<p>Code tersebut mirip dengan code sebelumnya pada bagian pembahasan <em>Covariance</em>. Perbedaannya adalah <em>Convariance</em> mengatur return type sedangkan <em>Contravariance</em> mengatur Argument. Selain itu, pada covariance nilai yang lebih general ada pada Supertype-nya, sedangkan pada <em>Contravariance</em> sebaliknya. Nilai yang lebih general ada pada <em>subtype</em>-nya. Oleh karena itu arah panahnya berlawanan.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739933023811/9b8830f0-632d-4c01-a859-29b5f27a8cef.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-kesimpulan">Kesimpulan</h3>
<p>Ketika membuat code, kita tidak selalu perlu mengimplentasikan Covariance atau Contravariance. Kita memerlukannya hanya pada saat memang diperlukan pengamanan pada tipe data kembalian method atau parameter. Agar integritas nilai yang masuk maupun keluar cukup terjaga.</p>
]]></content:encoded></item><item><title><![CDATA[Decentralize Git]]></title><description><![CDATA[Hashing
Git sederhananya merupakan simple Key-Value datastore, dimana hash dari content akan menjadi key untuk content itu sendiri. Baik itu hash dari content yang berisi meta commit, tree hash dan blob file. Setiap hash dalam git disebut dengan Obje...]]></description><link>https://verri.andriawan.web.id/decentralize-git</link><guid isPermaLink="true">https://verri.andriawan.web.id/decentralize-git</guid><category><![CDATA[Git]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[Hashing]]></category><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Sun, 26 Jan 2025 17:00:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1737470251658/30d918ef-38de-474c-bac5-cd6c585c6871.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-hashing">Hashing</h3>
<p>Git sederhananya merupakan simple Key-Value datastore, dimana hash dari content akan menjadi key untuk content itu sendiri. Baik itu hash dari content yang berisi meta commit, tree hash dan blob file. Setiap hash dalam git disebut dengan ObjectID, dimana dalam satu kali melakukan commit, git akan mengkalkulasi tiga hash sekaligus, yaitu hash untuk <strong>Meta Commit</strong> yang berisi message commit, tanggal commit dsb, lalu <strong>Tree Hash</strong>, dan <strong>Blob hash</strong>.</p>
<blockquote>
<p>Tulisan ini adalah bagian dari <a target="_blank" href="https://verri.andriawan.web.id/series/git-scm">Git SCM</a> Series, sehingga untuk bisa membaca secara menyeluruh dan berkesinambungan, alangkah lebih baik bisa membaca secara urut dari link series <a target="_blank" href="https://verri.andriawan.web.id/series/git-scm">berikut</a>.</p>
</blockquote>
<p>Sebagai contoh, kita coba membuat file bernama <a target="_blank" href="http://README.md"><code>README.md</code></a> yang berisi <code>Sample Content</code> <a target="_blank" href="http://README.md"><code>README.md</code></a>. Lalu kita add dan commit file tersebut dengan message commit <code>Sample Commit Message</code>. Setelah commit berhasil maka git akan memberikan hash SHA-1 yang merupakan hash dari content Meta Commit. Sebagai contoh seperti ini output hash seperti ini <code>df5496d7e785b44e2beef9349cef4bc3b4b9ddc7</code> atau bisa juga dengan 7 character awal dari hash tersebut (<code>df5496d</code>). Hash adalah key dari commit message yang kita buat. Cara mengetahui isi key tersebut adalah dengan menggunakan <code>git cat-file</code> . Sebagai contoh seperti ini</p>
<pre><code class="lang-bash">$ git cat-file -p df5496d
tree 5ffea0e1130ea4ecf542691c3e066e51d8628233
author Verri &lt;verri@local.host&gt; 1736952966 +0700
committer Verri &lt;verri@local.host&gt; 1736952966 +0700

Sample Commit Message
</code></pre>
<p>Seperti pada output dari hasil cat-file, key tersebut berisi <strong>Meta Commit</strong>.</p>
<p>Terlihat pada line pertama terdapat <code>tree &lt;hash&gt;</code> dimana hash tersebut berisi line permission file, type object lalu nama object. Jika menggunakan <code>git-cat-file</code> untuk membaca hash tersebut. Maka outputnya kurang lebih akan seperti ini.</p>
<pre><code class="lang-bash">$ git cat-file -p 5ffea0e1130ea4ecf542691c3e066e51d8628233
100644 blob 50e0aa820aae2d872340d0a13b94b129a9f589a8    README.md
</code></pre>
<p>Karena object berupa file, maka type object akan menjadi <code>blob</code> tapi jika object merupaka folder, maka type object akan menjadi <code>tree</code>.</p>
<p>Pada output dari hasil cat-file hash dari tree, terdapat hash yang merupakan key dari content <a target="_blank" href="http://README.md">README.md</a>. Bisa menggunakan cat-file untuk mendapatkan content dari key hash tersebut.</p>
<pre><code class="lang-bash">$ git cat-file -p 50e0aa820aae2d872340d0a13b94b129a9f589a8
Sample Content README.md
</code></pre>
<p>Terlihat dari output bahwa hash tersebut berisi snapshoot dari isi file <a target="_blank" href="http://README.md"><code>README.md</code></a> yang di-commit.</p>
<p>Karena Git menggunakan metode hashing untuk melakukan snapshoot setiap perubahan yang di-commit yang secara default menggunakan algorithm SHA-1, maka akan membuat git lebih flexible ketika didistribusikan karena dengan menggunakan metode ini, Git tidak akan tergantung pada machine yang digunakan.</p>
<h3 id="heading-shared-repository">Shared Repository</h3>
<p>Repository dalam Git merupakan kumpulan snapshoot dan history dari setiap perubahan yang di-commit. Ketika developer melakukan snapshooting dan commit dari perubahan code, Git akan me-hashing file-file yang terubah tersebut dan menyimpannya dalam bentuk blob file type pada folder <code>.git/objects</code>. Directory <code>.git</code> tersebutlah yang kita sebut <strong>Local Repository</strong>. Selanjutnya ketika developer perlu mendistribusikan perubahan-perubahan tersebut, maka diperlukan sebuah destinasi dimana nantinya git akan mendistribusikan file-file pada repository local tersebut ke destinasi yang dituju, dimana destinasi tersebut disebut sebagai <strong>Shared Repostiory</strong>. Seperti gambar di bawah ini.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737470429560/8a5882d6-c0e5-4227-af94-1c058654869e.png" alt class="image--center mx-auto" /></p>
<p>Dari gambar mungkin terlihat bahwa Git terkesan terpusat atau centralized. Tapi pada dasarnya karena Git menggunakan hashing sebagai metode untuk meng-signature setiap perubahan, sehingga memberikan kelebihan kepada Git untuk dapat <em>decentralize</em> / tidak terpusat. Developer bisa menambah berapapun remote repository yang diperlukan. Tergantung dari pada kesepakatan antar developer itu sendiri.</p>
<p>Sebagai contoh di sini, misal setiap developer bisa memiliki remote repository sendiri, ditambah satu buah remote repository master yang hanya boleh di-pull oleh developer. Dimana remote repository master ini sebagai tempat dimana semua feature yang stable dan sudah live di production.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737470548648/c473bf23-e397-47a6-b7f4-817ceda8c0b5.png" alt class="image--center mx-auto" /></p>
<p>Seperti terlihat pada gambar di atas. Terdapat satu master repository sebagai master repository, dimana maintainer / developer tidak memiliki hak untuk melakukan push ke master repository. Tapi setiap maintainer / developer hanya diperbolehkan melakukan push remote repository khusus developer. Dengan workflow seperti ini, maka setiap code yang masuk ke master repository akan terjaga secara history dan perubahan. Sedangkan pada remote repository masing-masing maintainer / developer dibebaskan untuk melakukan push biasa ataupun <em>force push</em>.</p>
<p><em>Histories</em> dan <em>snapshoot</em> pada setiap Shared Repository bisa saja berbeda-beda, tergantung pada masing-masing developer. Misal Developer A hanya akan meng-<em>update</em> branch <code>feature/sidebar</code> pada shared repository B, begitu juga sebaliknya. Developer B mungkin hanya meng-<em>update</em> branch <code>feature/header</code>. Tapi bisa juga Shared Repository A atau pun Shared Repository B identik yaitu memiliki history dan jumlah Branch yang bener-benar sama. Kembali lagi semua tergantung pada kesepatakan antar developer yang terlibat.</p>
<p>Dengan kemampuan Git yang tidak terpusat, maka akan mempermudah developer untuk saling sharing code tanpa perlu khawatir dengan downtime dari Server.</p>
<p>reference:</p>
<ul>
<li><p><a target="_blank" href="https://www.geeksforgeeks.org/introduction-to-hashing-2/">https://www.geeksforgeeks.org/introduction-to-hashing-2/</a></p>
</li>
<li><p><a target="_blank" href="https://git-scm.com/docs/hash-function-transition">https://git-scm.com/docs/hash-function-transition</a></p>
</li>
<li><p><a target="_blank" href="https://git-scm.com/book/en/v2/Distributed-Git-Distributed-Workflows">https://git-scm.com/book/en/v2/Distributed-Git-Distributed-Workflows</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Open Closed Principle (OCP)]]></title><description><![CDATA[Prinsip yang pertama kali dikenalkan oleh Bertrand Meyer pada buku Object Oriented Software Construction pada tahun 1988. Idea dasar dari Prinsip ini adalah Software Entity baik itu class, module, function terbuka untuk penambahan feature baru tanpa ...]]></description><link>https://verri.andriawan.web.id/open-closed-principle-ocp</link><guid isPermaLink="true">https://verri.andriawan.web.id/open-closed-principle-ocp</guid><category><![CDATA[clean code]]></category><category><![CDATA[SOLID principles]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[abstraction]]></category><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Fri, 24 Jan 2025 17:00:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748694444051/7855e14b-60c9-49fd-a9e3-e2d47ca5fa10.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Prinsip yang pertama kali dikenalkan oleh Bertrand Meyer pada buku <em>Object Oriented Software Construction</em> pada tahun 1988. Idea dasar dari Prinsip ini adalah Software Entity baik itu class, module, function terbuka untuk penambahan feature baru tanpa merubah code yang ada. Sebagai contoh adalah seperti berikut ini.</p>
<blockquote>
<p>Tulisan ini adalah bagian dari <a target="_blank" href="https://verri.andriawan.web.id/series/solid-principles">SOLID Principles</a> Series, sehingga untuk bisa membaca secara menyeluruh dan berkesinambungan, alangkah lebih baik bisa membaca secara urut dari link series <a target="_blank" href="https://verri.andriawan.web.id/series/solid-principles">berikut</a>.</p>
</blockquote>
<p>Asumsi kita sudah membuat class untuk Notification dengan nama file <code>NotifyEvent.ts</code> dengan content seperti berikut:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> NotifyEvent {

    <span class="hljs-keyword">private</span> email: <span class="hljs-built_in">string</span>
    <span class="hljs-keyword">private</span> phone: <span class="hljs-built_in">string</span>
    <span class="hljs-keyword">private</span> id: <span class="hljs-built_in">string</span>
    <span class="hljs-keyword">private</span> content: <span class="hljs-built_in">string</span>

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">new</span>(data: Record&lt;<span class="hljs-built_in">string</span>, <span class="hljs-built_in">any</span>&gt;) : NotifyEvent {
        <span class="hljs-keyword">const</span> instance = <span class="hljs-keyword">new</span> NotifyEvent
        instance.setEntity(data)
        <span class="hljs-keyword">return</span> instance
    }

    setEntity(data: Record&lt;<span class="hljs-built_in">string</span>,<span class="hljs-built_in">any</span>&gt;) {
        <span class="hljs-built_in">this</span>.id = data.id
        <span class="hljs-built_in">this</span>.email = data.email
        <span class="hljs-built_in">this</span>.phone = data.phone
        <span class="hljs-built_in">this</span>.content = data.content
    }

    sendEmail() {
        <span class="hljs-comment">// code untuk mengirim email</span>
    }

    sendSMS() {
        <span class="hljs-comment">// code untuk mengirim sms</span>
    }

}
</code></pre>
<p>Class tersebut hanya memiliki tugas yang yaitu <code>Notification</code>. Tapi class ini bermasalah atau orang biasanya menyebutnya tidak fleksible. Kenapa? karena, seandainya kita ingin menambah fitur notifikasi baru untuk mengirim infomasi ke Telegram Messanger misalnya, maka kita harus merubah class tersebut untuk menambah method baru semisal bernama <code>sendTelegram</code> . Jika kita mengimplementasikan Prinsip <em>OpenClose</em>, Code class di atas akan menjadi seperti ini:</p>
<p>Pertama kita perlu melakukan abstraction agar semua method yang required, diharuskan diimplementasi. Kita bisa menggunakan abtract class atau kita bisa menggunakan Interface. Tapi pada tulisan ini saya akan menggunakan Class Abstraction. Tapi apa bedanya? Saya akan buat tulisan khusus untuk membahas itu. Berikut contoh abstract cclass yang kita gunakan.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">abstract</span> <span class="hljs-keyword">class</span> NotifyEvent {
    <span class="hljs-keyword">abstract</span> setProp(data: Record&lt;<span class="hljs-built_in">string</span>,<span class="hljs-built_in">any</span>&gt;): <span class="hljs-built_in">void</span>
    <span class="hljs-keyword">abstract</span> log(): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt;
    <span class="hljs-keyword">abstract</span> send(): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">boolean</span>&gt;
}
</code></pre>
<p>Buat class event yang extends dari class NotifyEvent tersebut. Sebagai contoh di sini akan membuat event notifier untuk SMS.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> NotifySMSEvent <span class="hljs-keyword">extends</span> NotifyEvent {
    <span class="hljs-keyword">private</span> phone: <span class="hljs-built_in">string</span>

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">new</span>(data: Record&lt;<span class="hljs-built_in">string</span>,<span class="hljs-built_in">any</span>&gt;): NotifyEvent {
        <span class="hljs-keyword">const</span> instance = <span class="hljs-keyword">new</span> NotifySMSEvent
        instance.setProp(data)
        <span class="hljs-keyword">return</span> instance
    }

    setProp(data: Record&lt;<span class="hljs-built_in">string</span>, <span class="hljs-built_in">any</span>&gt;) {
        <span class="hljs-comment">// set property</span>
    }

    <span class="hljs-keyword">async</span> log() {
      <span class="hljs-comment">// Log to database</span>
    }

    <span class="hljs-keyword">async</span> send() {
        <span class="hljs-comment">// Send Code di sini</span>
    }
}
</code></pre>
<p>Lalu kita buat dispatcher class atau executor untuk notifier SMS diatas.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> NotifyDispatcher {
    <span class="hljs-keyword">private</span> events: NotifyEvent[] = []; 

    <span class="hljs-keyword">async</span> execute() {
      <span class="hljs-keyword">const</span> events: <span class="hljs-built_in">any</span>[] = []
      <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> e <span class="hljs-keyword">of</span> <span class="hljs-built_in">this</span>.events) {
         events.push(e,send(), e.log())
      }
      <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.all(events)
    }    

    pushEvent(event: NotifyEvent) {
        <span class="hljs-built_in">this</span>.events.push(event)
    }
}
</code></pre>
<p>Class dipatcher inilah yang seharusnya ada pada bagian core dengan tujuan agar tidak ada perubahan pada core atau module utama ketika business menginginkan penambahan feature untuk notification.</p>
<p>Selanjutnya pada function utama, kita perlu untuk meng-initialisasi class-class diatas. Seperti berikut.</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-keyword">const</span> data = {} 
        <span class="hljs-keyword">const</span> event: NotifyEvent = NotifySMSEvent.new(data)

        <span class="hljs-keyword">const</span> dispatcher= <span class="hljs-keyword">new</span> NotifyDispatcher
        dispatcher.pushEvent(event)

        <span class="hljs-keyword">await</span> dispatcher.execute()
}
</code></pre>
<h3 id="heading-kesimpulan">Kesimpulan</h3>
<p>Dengan mengimplementasikan OCP, maka program akan lebih flexible dalam melakukan pergantian usecase module yang akan digunakan.</p>
<p>references:</p>
<ul>
<li><p><a target="_blank" href="https://web.uettaxila.edu.pk/CMS/AUT2011/seSCbs/tutorial/Object%20Oriented%20Software%20Construction.pdf">https://web.uettaxila.edu.pk/CMS/AUT2011/seSCbs/tutorial/Object Oriented Software Construction.pdf</a></p>
</li>
<li><p><a target="_blank" href="https://stackify.com/solid-design-open-closed-principle/">https://stackify.com/solid-design-open-closed-principle</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Remote Shared Repository di VPS]]></title><description><![CDATA[VPS atau Virutal Private Server adalah server non fisik yang biasa digunakan untuk manjalankan service-service secara bebas-terbatas yang disediakan penyedia layanan kepada penyewa. Dengan menggunakan VPS, kita bisa mendapatkan akses console dan root...]]></description><link>https://verri.andriawan.web.id/remote-shared-repository-di-vps</link><guid isPermaLink="true">https://verri.andriawan.web.id/remote-shared-repository-di-vps</guid><category><![CDATA[Git]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[Source code management ]]></category><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Thu, 23 Jan 2025 17:00:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1737468300250/5a01629f-f3b1-44a8-90a9-bbfacabb2e1c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>VPS atau Virutal Private Server adalah server non fisik yang biasa digunakan untuk manjalankan service-service secara bebas-terbatas yang disediakan penyedia layanan kepada penyewa. Dengan menggunakan VPS, kita bisa mendapatkan akses <code>console</code> dan <code>root access</code> pada server yang disewa tersebut. Sehingga kita bisa meng-<em>install</em> apapun di dalamnya termasuk juga <code>git command</code> dan OpenSSH Server.</p>
<blockquote>
<p>Tulisan ini adalah bagian dari <a target="_blank" href="https://verri.andriawan.web.id/series/git-scm">Git SCM</a> Series, sehingga untuk bisa membaca secara menyeluruh dan berkesinambungan, alangkah lebih baik bisa membaca secara urut dari link series <a target="_blank" href="https://verri.andriawan.web.id/series/git-scm">berikut</a>.</p>
</blockquote>
<p>Metode untuk membuat remote repository pada VPS sama seperti saat membuat remote repository pada lokal folder. Perbedaannya, ketika kita men-setup Remote repository pada server, maka untuk kita dapat melakukan cloning repository tersebut ke lokal device, kita perlu cloning menggunakan SSH. Namun sudah bisa dipastikan setiap kita menyewa VPS, pasti sudah terdapat SSH Server. Sehingga yang perlu kita setup hanya Git Command di VPS yang kita sewa.</p>
<p>Untuk mengginstall git command cukup dengan mejalankan command di terminal server seperti berikut.</p>
<p><strong>Menggunakan Apt Command</strong></p>
<pre><code class="lang-bash">$ sudo apt update
$ sudo apt install -y git
</code></pre>
<p><strong>Menggunakan Yum / dnf</strong></p>
<pre><code class="lang-bash">$ sudo yum update
$ sudo yum install git
</code></pre>
<p>Jika GIt Command sudah berhasil diinstall, selanjutnya kita perlu membuat bare repository di VPS. Langkahnya sama seperti pada bare repository pada lokal.</p>
<pre><code class="lang-bash">$ mkdir -p /opt/repos/code.git
$ <span class="hljs-built_in">cd</span> /opt/repos/code.git
$ git init --bare
</code></pre>
<p>Ketika mencoba clone repository tersebut, maka bisa melakukan clone ke local device dengan menggunakan SSH Authentication. Sebagai contoh di sini, user ssh menggunakan user <code>root</code> dengan contoh public IP server <code>10.0.2.1</code>. Maka command untuk clone adalah seperti ini</p>
<pre><code class="lang-tsx">$ git clone root@10.0.2.1:/opt/repos/code.git code
</code></pre>
<p>Kita juga bisa menambahkan remote repository tersebut pada workspace git yang sudah ada di lokal device. Sebagai contoh kita menggunakan workspace code yang sudah dibuat sebelumnya yaitu <code>/tmp/sample</code>. Maka untuk menambahkan remote repository yang ada di VPS bisa dengan menjalakan command berikut.</p>
<pre><code class="lang-bash">$ <span class="hljs-built_in">cd</span> /tmp/sample
$ git remote add vps root@10.0.2.1:/opt/repos/code.git
</code></pre>
<p>Untuk push semua <a target="_blank" href="https://www.notion.so/Understanding-Git-Commit-147725d954e58031ac6bff3ae8e0f5c6?pvs=21">commit</a> yang sudah dibuat pada workspace tersebut maka tinggal menjalankan command dengan format <code>git push [nama remote repo] [nama branch]</code>. Pada contoh di sini nama branch yang ada adalah <code>main</code>. Maka untuk push ke remote repository bisa dengan cara seperti di bawah ini.</p>
<pre><code class="lang-bash">$ git push vps main
</code></pre>
<p>Sampai pada tahap ini, kita berhasil membuat remote repository di VPS. Tapi terasa kurang nyaman ketika melakukan push ke Remote Repository, karena setiap melakukan push, kita perlu memasukan password SSH.</p>
]]></content:encoded></item><item><title><![CDATA[Single Reponsibility Principle (SRP)]]></title><description><![CDATA[SRP (Single Reponsibility Principle) term pertama dalam SOLID principles. Gagasan utama dari prinsip ini yaitu sebuah class seharusnya hanya memilki satu tugas dan hanya boleh memiliki satu alasan untuk dirubah. Mungkin kalimat yang cukup membingungk...]]></description><link>https://verri.andriawan.web.id/single-reponsibility-principle-srp</link><guid isPermaLink="true">https://verri.andriawan.web.id/single-reponsibility-principle-srp</guid><category><![CDATA[SOLID principles]]></category><category><![CDATA[abstraction]]></category><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Wed, 22 Jan 2025 17:00:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748694285347/79643d46-4886-4963-b644-5309a78da01b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>SRP (<em>Single Reponsibility Principle</em>) term pertama dalam SOLID principles. Gagasan utama dari prinsip ini yaitu sebuah class seharusnya hanya memilki satu tugas dan hanya boleh memiliki satu alasan untuk dirubah. Mungkin kalimat yang cukup membingungkan, tapi akan coba dijelaskan pada tulisan ini. Tapi sebelum itu, inilah gambaran besar ide yang akan kita gunakan pada series ini. Kita akan menggunakan ide <strong>Vending Machine</strong> sederhana sebagai ide dasarnya.</p>
<blockquote>
<p>Tulisan ini adalah bagian dari <a target="_blank" href="https://verri.andriawan.web.id/series/solid-principles">SOLID Principles</a> Series, sehingga untuk bisa membaca secara menyeluruh dan berkesinambungan, alangkah lebih baik bisa membaca secara urut dari link series <a target="_blank" href="https://verri.andriawan.web.id/series/solid-principles">berikut</a>.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737472221520/a7447704-1404-44b6-abc4-8cd56fbdfcfc.png" alt class="image--center mx-auto" /></p>
<p>Seperti yang terlihat pada flow pada di atas, ketika pengguna selesai memilih produk, maka <em>Vending Machine</em> akan mengupdate info stock dan mengirim notifikasi ke admin dashboard. Jika diubah ke dalam bentuk code, maka programmer kurang lebih akan membuat code seperti ini.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> OrderService {

    <span class="hljs-keyword">async</span> execute(dto: RequestDTO) {}

    validatePayload(dto: RequestDTO) {
        <span class="hljs-comment">// Memvalidasi setiap key dan value yang dikirim dari sisi client</span>
    }

    <span class="hljs-keyword">async</span> getProduct() {
        <span class="hljs-comment">// Meng-query database langsung disini untuk mendapatkan info product</span>
    }

    calculateNewAvailableStock() {
        <span class="hljs-comment">// Mengurangi jumlah stock</span>
    }

    <span class="hljs-keyword">async</span> saveUpdateStock() {
        <span class="hljs-comment">// Meng-query database untuk menyimpan perubahan di database</span>
    }

    <span class="hljs-keyword">async</span> sendNotifyAdmin() {
        <span class="hljs-comment">// Mengirim notifikasi ke email atau sms</span>
    }
}
</code></pre>
<p>Seperti yang terlihat pada code di atas, class tersebut memiliki banyak tugas di dalamnya, sehingga class tersebut tidak memiliki kejelasan tugas pokoknya. Query ke sql, notifikasi dan logic usecase order ada dalam satu class mengakibatkan class tersebut terlihat kompleks. Akan menjadi lebih kompleks jika feature semakin banyak akibat dari keharusan aplikasi untuk mengikuti perkembangan bisnis dari perusahaan tersebut. Semakin kompleks sistem, jika tidak di-manage dengan baik, maka akan menjadi lebih sulit untuk tracing issue ketik terjadi error.</p>
<p>Akibat yang sering terjadi adalah, programmer akan kurang percaya diri atau khawatir ketika code tersebut akan dideploy ke production. Lebih jauh lagi, karena terlalu kompleks, akhirnya programmer memutuskan untuk merubah keseluruhan class tersebut, yang akan membuat probabilitas error pada program semakin besar. Akan memperparah probabilitas error dengan tidak adanya unit test atau <em>integration test.</em></p>
<p>Lalu bagaimana agar code tersebut agar mudah dimaintain/diurus? Yaitu dengan cara memisahkan logic menjadi class-class yang sesuai dengan tugasnya. Seperti code ini.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">class</span> OrderUsecase {
    <span class="hljs-keyword">async</span> execute(dto: OrderRequestDTO): <span class="hljs-built_in">Promise</span>&lt;OrderResponseDTO&gt; {}
    validate(dto: OrderReqeustDTO): <span class="hljs-built_in">boolean</span> {}
    <span class="hljs-keyword">async</span> getStock(id: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">Promise</span>&lt;ProductEntity&gt; {}
    calculateStock(product: ProductEntity) {}
}

<span class="hljs-keyword">class</span> OrderRepository {
    <span class="hljs-keyword">async</span> getProductById(id: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">Promise</span>&lt;ProductEntity&gt; {}
    <span class="hljs-keyword">async</span> updateProduct(id: <span class="hljs-built_in">number</span>, product: ProductEntity): <span class="hljs-built_in">Promise</span>&lt;ProductEntity&gt; {}
}

<span class="hljs-keyword">class</span> OrderNotification() {
    <span class="hljs-keyword">async</span> sendAsWebsocket() {}
    <span class="hljs-keyword">async</span> sendAsSMS() {}
}
</code></pre>
<p>Bisa kita lihat dari code di atas, setiap class sekarang memiliki tugasnya yang lebih spesifik. Misal class <code>OrderUsecase</code> hanya memiliki satu tugas untuk alur utama usecase order, untuk class <code>OrderRepository</code> digunakan khusus untuk yang berhubungan dengan database, dan untuk class <code>OrderNotification</code> tugasnya hanya yang berhubungan dengan notifikasi. Sebagaimana syarat SRP, class hanya boleh miliki satu tugas saja.</p>
<p>Lalu dimisalkan perlu perubahan pada logic khusus <em>Order Usecase</em>, berarti programmer hanya perlu merubah class <code>OrderUsecase</code> saja, tidak perlu merubah class lain. Jika ingin merubah yg berhubungan dengan database, berarti yang dirubah bukan class <code>OrderUsecase</code> melainkan <code>OrderRepository</code>. Sesuai dengan syarat SRP, class hanya boleh dirubah karena satu alasan saja.</p>
<h3 id="heading-kesimpulan">Kesimpulan</h3>
<p>Single Responsibiliy Princple akan memperjelas tugas dari setiap class. Semakin jelas tugas sebuah class, maka akan semamkin mempermudah programmer ketika akan melakukan tracing masalah.</p>
<p>Selanjutnya akan membahas tentang point ketiga dari SOLID, yaitu <a target="_blank" href="https://verri.andriawan.web.id/open-closed-principle-ocp-cm6b0coqh000k0al720ci35sh?source=more_series_bottom_blogs">Open Closed Principle</a></p>
<p><strong>reference:</strong></p>
<ul>
<li><a target="_blank" href="https://verri.andriawan.web.id/single-reponsibility-principle-srp">http://www.but</a><a target="_blank" href="http://www.butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod">unclebob.com/ArticleS.UncleBob.PrinciplesOfOod</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[S.O.L.I.D Principles]]></title><description><![CDATA[Setiap programmer pasti memiliki style coding yang berbeda-beda. Kebebasan dalam menulis code tersebut dapat membuat code tidak terstruktur yang berimbas pada aplikasi yang sulit dimaintain, di-monitor bahkan di-trace ketika terjadi error. Maka dari ...]]></description><link>https://verri.andriawan.web.id/solid-principles</link><guid isPermaLink="true">https://verri.andriawan.web.id/solid-principles</guid><category><![CDATA[SOLID principles]]></category><category><![CDATA[abstraction]]></category><category><![CDATA[clean code]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Wed, 22 Jan 2025 01:18:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748694513308/dc6cae6b-47a6-4f25-9953-984e22f045b3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Setiap programmer pasti memiliki style coding yang berbeda-beda. Kebebasan dalam menulis code tersebut dapat membuat code tidak terstruktur yang berimbas pada aplikasi yang sulit dimaintain, di-monitor bahkan di-trace ketika terjadi error. Maka dari itu setiap developer harus memahami prinsip-prinsip dalam menulis code. Salah satunya adalah <strong>SOLID</strong>. Dengan memahami dan mentaati aturan dari <strong>SOLID</strong>, maka Style code dan abstraksi dari code akan lebih mudah dipahami dan lebih terstruktur.</p>
<p>Jadi seperti apa itu <strong>SOLID</strong> ? saya akan coba detailkan satu persatu di tulisan yang saya buat ini untuk setiap prinsip-prinsip pada setiap huruf pada <strong>SOLID</strong>.</p>
<blockquote>
<p>Tulisan ini adalah bagian dari <a target="_blank" href="https://verri.andriawan.web.id/series/solid-principles">SOLID Principles</a> Series, sehingga untuk bisa membaca secara menyeluruh dan berkesinambungan, alangkah lebih baik bisa membaca secara urut dari link series <a target="_blank" href="https://verri.andriawan.web.id/series/solid-principles">berikut</a>.</p>
</blockquote>
<p><strong>SOLID</strong> merupakan sebuah singkatan dari beberapa prinsip, yaitu:</p>
<ul>
<li><p>S - Single Responsibility Principle</p>
</li>
<li><p>O - Open Closed Principle</p>
</li>
<li><p>L - Liskov Subtition Principle</p>
</li>
<li><p>I - Interface Segregation Principle</p>
</li>
<li><p>D - Dependency Inversion Principle</p>
</li>
</ul>
<p>Selanjutnya akan membahas tentang point pertama dari SOLID, yaitu <a target="_blank" href="https://verri.andriawan.web.id/single-reponsibility-principle-srp">Single Responsibility Principle</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Git Shared Repository]]></title><description><![CDATA[Sebagai programmer pasti tidak asing dengan yang namanya Git SCM. Karena Git SCM sangat populer dan banyak digunakan oleh programmer sebagai tool untuk mengontrol version dari code. Sebelum adanya Git, ada tool sejenis dengan nama SVN, sayangnya SVN ...]]></description><link>https://verri.andriawan.web.id/git-shared-repository</link><guid isPermaLink="true">https://verri.andriawan.web.id/git-shared-repository</guid><category><![CDATA[Git]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[Source code management ]]></category><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Tue, 21 Jan 2025 13:54:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1737468019412/507cbbf2-c862-44c8-9985-0906986fd457.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Sebagai programmer pasti tidak asing dengan yang namanya Git SCM. Karena Git SCM sangat populer dan banyak digunakan oleh programmer sebagai tool untuk mengontrol version dari code. Sebelum adanya Git, ada tool sejenis dengan nama SVN, sayangnya SVN memiliki keterbatasan salah satunya ketika Remote Repository SVN berada di tempat yang tidak bisa diakses. Keterbatasan itu diatasi oleh Git dengan fitur decentralize-nya. Oleh karena itu, pada tulisan ini saya akan memperkenalkan fitur menarik untuk digunakan yang berhubungan juga dengan fitur kerennya Git yaitu decentrelize-nya.</p>
<blockquote>
<p>Tulisan ini adalah bagian dari <a target="_blank" href="https://verri.andriawan.web.id/series/git-scm">Git SCM</a> Series, sehingga untuk bisa membaca secara menyeluruh dan berkesinambungan, alangkah lebih baik bisa membaca secara urut dari link series <a target="_blank" href="https://verri.andriawan.web.id/series/git-scm">berikut</a>.</p>
</blockquote>
<p>Dari yang saya amati, sampai saat ini masih banyak programmer yang berfikir bahwa untuk bisa mendistribusikan code menggunakan Git, harus menggunakan layanan seperti Github, Gitlab, Bitbucket atau yang menggunakan self-manage git server seperti Gogs atau sejenisnya. Tapi ternyata, membuat git remote repository bisa menggunakan local folder pada device atau VPS dengan SSH. Berikut ini adalah langkah-langkahnya.</p>
<h3 id="heading-shared-repository-di-local-drive">Shared Repository di Local Drive</h3>
<p>Sedikit mengklarifikasi istilah dalam Git seperti repository pada Git adalah kumpulan snapshoot dari perubahan-perubahan pada directory workspace yang di-<em>commit</em> oleh developer. Commit itu sendiri merupakan cara developer melakukan snapshoot pada setiap perubahan yang terjadi pada workspace. Pada section ini kita coba membuat Shared Repository dengan hanya membedakan directory.</p>
<p>Sebelum masuk ke bagaimana cara men-setup shared repository, kita perlu mempersiapkan dulu folder yang berisi git yang akan kita push ke Shared Repository dengan langkah seperti berikut.</p>
<p>Buat folder baru dan tambahkan file <code>README.md</code> pada folder tersebut.</p>
<pre><code class="lang-bash">$ mkdir /tmp/sample
$ <span class="hljs-built_in">cd</span> /tmp/sample
$ <span class="hljs-built_in">echo</span> <span class="hljs-string">"# Initial Git Workspace"</span> &gt; README.md
</code></pre>
<p>Init folder tersebut sebagai git dan commit file <code>README.md</code> tersebut.</p>
<pre><code class="lang-bash">$ git init .
$ git add .
$ git commit -am <span class="hljs-string">"Initialize project"</span>
$ git status
</code></pre>
<p>Setelah git status dijalankan, maka pada terminal akan tampil informasi current branch dimana defaultnya adalah branch main.</p>
<p>Sederhananya, membuat local Shared Repository itu sama seperti saat mempersiapkan code yang akan dipush ke repository pada langkah sebelumnya. Tapi bedannya, pada saat initial git pada folder tersebut, perlu menambahkan option <code>--bare</code>. Seperti berikut.</p>
<pre><code class="lang-bash">$ mkdir -p /opt/repos/code.git
$ <span class="hljs-built_in">cd</span> /opt/repos/code.git
$ git init --bare
</code></pre>
<p>Berdasarkan official documentation mengenai option <code>--bare</code>.</p>
<blockquote>
<p>Create a bare repository. If GIT_DIR environment is not set, it is set to the current working directory.</p>
</blockquote>
<p>Bahwa option <code>--bare</code> digunakan untuk membuat <em>Shared Repository</em> baru yang masih kosong. Jika <code>GIT_DIR</code> tidak diset, maka git akan men-set <em>repository</em> baru tersebut pada direktori dimana <em>cursor</em> berada saat perintah git init dijalankan.</p>
<p>Selanjutnya hanya perlu meset Shared Repository config pada folder code yang sudah dipersiapkan sebelumnya. Dengan langkah seperti berikut.</p>
<pre><code class="lang-bash">$ <span class="hljs-built_in">cd</span> /tmp/sample
$ git remote add origin /opt/repos/code.git
$ git push origin main
</code></pre>
<p>Jika kita coba melakukan clone dari folder git repository tersebut, maka kita akan langsung mendapatkan file <code>README.md</code> yang sudah kita <em>commit</em> pada folder <code>/tmp/sample</code>.</p>
<pre><code class="lang-bash">$ <span class="hljs-built_in">cd</span> /tmp/
$ git <span class="hljs-built_in">clone</span> /opt/repos/code.git code
$ ls code
README.md
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Multi Host SSH Config]]></title><description><![CDATA[Tulisan ini adalah bagian dari Remote SSH Series, sehingga untuk bisa membaca secara menyeluruh dan berkesinambungan, alangkah lebih baik bisa membaca secara urut dari link series berikut.

Pada tulisan dengan title Alias untuk Remote SSH, kita sudah...]]></description><link>https://verri.andriawan.web.id/multi-host-ssh-config</link><guid isPermaLink="true">https://verri.andriawan.web.id/multi-host-ssh-config</guid><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Thu, 19 Dec 2024 00:55:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748695117271/649b1476-6abc-49de-9109-32659cc20f25.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Tulisan ini adalah bagian dari <a target="_blank" href="https://verri.andriawan.web.id/series/ssh-remote-server"><strong>Remote SSH</strong></a> Series, sehingga untuk bisa membaca secara menyeluruh dan berkesinambungan, alangkah lebih baik bisa membaca secara urut dari link series <a target="_blank" href="https://verri.andriawan.web.id/series/ssh-remote-server">berikut</a>.</p>
</blockquote>
<p>Pada tulisan dengan title <a target="_blank" href="https://verri.andriawan.web.id/alias-host-untuk-remote-ssh">Alias untuk Remote SSH</a>, kita sudah mencoba membuat alias domain untuk SSH agar lebih simple ketika akan meremote server. Hanya dengan command <code>ssh repository.aws</code>, SSH sudah bisa mengetahui config apa yang harus digunakan. Tapi kita masih ada masalah di config SSH pada local device.</p>
<p>Kita sudah mengetahui bahwa untuk mengconfig alias domain pada SSH dengan menambah Host baru pada file <code>~/.ssh/config</code>.</p>
<pre><code class="lang-bash">Host repository.aws
    User root
    IdentityFile ~/.ssh/id_ed25519
</code></pre>
<p>Tapi ketika kita perlu menambah destination host baru yang memerlukan key yang sama kita harus menambah list Host tersebut ke list yang sama misal seperti ini.</p>
<pre><code class="lang-bash">Host repository.aws web.aws api.aws
    User root
    IdentityFile ~/.ssh/id_ed25519
</code></pre>
<p>Semakin banyak destination server, semakin panjang list-nya. Cukup merepotkan bukan?!</p>
<p>Nah pada tulisan sebelumnya dengan title “Alias untuk Remote SSH” saya menyarankan sebaiknya menggunakan extesion domain. Karena untuk keperluan multi destination seperti ini. Dengan menggunakan extension domain, akan mempermudah kita melisting host alias. Sebagai contoh seperti ini.</p>
<pre><code class="lang-bash">Host *.aws
    User root
    IdentityFile ~/.ssh/id_ed25519
</code></pre>
<p>Dengan menggunakan asterick (*), maka semua alias host dengan menggunakan extension <code>.aws</code> akan menggunakan config tersebut. Tapi bisa saja menggunakan IP Address, misalkan seperti ini.</p>
<pre><code class="lang-bash">Host 192.168.*
    User root
    IdentityFile ~/.ssh/id_ed25519
</code></pre>
<p>Tapi jika menggunakan IP Address, jelas kita akan sulit mengingatnya. Maka dari itu perlu alias host agar lebih mudah mengingat destination nya.</p>
<h2 id="heading-kesimpulan">Kesimpulan</h2>
<p>Dengan menggunakan asterisk (*), kita bisa mengernalkan config ssh. Dengan begitu akan lebih mudah memaintain IdentityFile untuk tiap-tiap host.</p>
]]></content:encoded></item><item><title><![CDATA[Alias host untuk Remote SSH]]></title><description><![CDATA[Tulisan ini adalah bagian dari Remote SSH Series, sehingga untuk bisa membaca secara menyeluruh dan berkesinambungan, alangkah lebih baik bisa membaca secara urut dari link series berikut.

Pada tutorial sebelumnya dengan title SSH tanpa Password sud...]]></description><link>https://verri.andriawan.web.id/alias-host-untuk-remote-ssh</link><guid isPermaLink="true">https://verri.andriawan.web.id/alias-host-untuk-remote-ssh</guid><category><![CDATA[ssh]]></category><category><![CDATA[configuration]]></category><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Tue, 17 Dec 2024 13:49:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748695047544/401cacf5-26e9-4561-a0cc-b8bfc086c098.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Tulisan ini adalah bagian dari <a target="_blank" href="https://verri.andriawan.web.id/series/ssh-remote-server"><strong>Remote SSH</strong></a> Series, sehingga untuk bisa membaca secara menyeluruh dan berkesinambungan, alangkah lebih baik bisa membaca secara urut dari link series <a target="_blank" href="https://verri.andriawan.web.id/series/ssh-remote-server">berikut</a>.</p>
</blockquote>
<p>Pada tutorial sebelumnya dengan title <a target="_blank" href="https://verri.andriawan.web.id/ssh-tanpa-password">SSH tanpa Password</a> sudah dijabarkan step by step bagaimana cara mensetupnya. Pada tulisan kali ini saya akan menunjukan bagaimana membuat agar kita bisa melakukan autentikasi ke server dengan command lebih sederhana. Misal seperti ini.</p>
<pre><code class="lang-bash">$ ssh repository.aws
</code></pre>
<p>Seperti terlihat dari contoh command ssh tersebut, tidak ada user, ip address dan juga identity file (-i) yang diikutkan sebagai argument command.</p>
<blockquote>
<p>Dengan cara ini, maka user, IP Address dan juga identity File tidak kan tercatat pada history terminal <code>history -c</code> kemana user melakukan remote ssh.</p>
</blockquote>
<p>Untuk bisa membuat seperti itu, pertama yang perlu dilakukan adalah dengan mambuat alias domain di file <code>/etc/hosts</code>.</p>
<pre><code class="lang-bash">$ <span class="hljs-built_in">echo</span> <span class="hljs-string">"repository.aws\\t10.2.0.2"</span> | sudo tee -a /etc/hosts
</code></pre>
<p>Pada command tersebut saya menggunakan <a target="_blank" href="http://repository.aws"><code>repository.aws</code></a> sebagai domain, nama alias domain ini bebas, bisa apa saja. misal kita bisa menggunakan nama <code>repository</code>, tanpa domain extention. Hal tersebut sah-sah saja. Namun lebih baik menggunakan extension karena ada keunggulan yg nantinya akan saya jelaskan pada tulisan ini juga.</p>
<p>Untuk IP address, saya menggunakan destination ip address <code>10.2.0.2</code>. Dimana IP ini seharusnya IP Address dari remote server yang akan diremote.</p>
<p>Setelah selesai menambah alias dari IP Address tersebut. Kita bisa memvalidasi apakah alias sudah ter-setup dengan bensar, dengan cara ping ke domain baru tersebut.</p>
<pre><code class="lang-bash">$ ping repository.aws
64 bytes from 10.2.0.2: icmp_seq=0 ttl=64 time=0.108 ms
64 bytes from 10.2.0.2: icmp_seq=1 ttl=64 time=0.160 ms
64 bytes from 10.2.0.2: icmp_seq=2 ttl=64 time=0.162 ms
64 bytes from 10.2.0.2: icmp_seq=3 ttl=64 time=0.175 ms
</code></pre>
<p>Terlihat bahwa response memberikan info <code>from 10.2.0.2</code> menandakan bahwa domain sudah tersetup dengan benar. Langkah selanjutnya menambah config untuk SSH pada file <code>~/.ssh/config</code> untuk domain baru. Seperti berikut.</p>
<pre><code class="lang-bash">Host repository.aws
    User root
    IdentityFile ~/.ssh/id_ed25519
</code></pre>
<p>Sampai langkah ini, short SSH command sudah bisa dilakukan. Jika kita mengeksekusi command ssh seperti berikut:</p>
<pre><code class="lang-bash">$ ssh repository.aws
</code></pre>
<p>Maka kita akan mendapatkan shell dari remote server yang dituju.</p>
<h2 id="heading-kesimpulan">Kesimpulan</h2>
<p>Kita bisa menyebunyikan argument-argument pada command ssh dengan men-config file <code>~/.ssh/config</code> dengan begitu, akan lebih memudahkan kita sebagai commander yang mengeksekusi command ssh tersebut karena kita hanya perlu mengingat nama alias dari pada harus mengingat IP Address dan argument-argument lainnya.</p>
]]></content:encoded></item><item><title><![CDATA[Remote SSH tanpa Password]]></title><description><![CDATA[Tulisan ini adalah bagian dari Remote SSH Series, sehingga untuk bisa membaca secara menyeluruh dan berkesinambungan, alangkah lebih baik bisa membaca secara urut dari link series berikut.

Me-remote device atau khususnya server biasa dilakuan oleh p...]]></description><link>https://verri.andriawan.web.id/ssh-tanpa-password</link><guid isPermaLink="true">https://verri.andriawan.web.id/ssh-tanpa-password</guid><category><![CDATA[ssh]]></category><category><![CDATA[ssh-keys]]></category><category><![CDATA[Passwordless]]></category><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Sun, 15 Dec 2024 07:44:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748695091443/24d950e9-d166-408b-9f1e-8197cf05a799.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Tulisan ini adalah bagian dari <a target="_blank" href="https://verri.andriawan.web.id/series/ssh-remote-server"><strong>Remote SSH</strong></a> Series, sehingga untuk bisa membaca secara menyeluruh dan berkesinambungan, alangkah lebih baik bisa membaca secara urut dari link series <a target="_blank" href="https://verri.andriawan.web.id/series/ssh-remote-server">berikut</a>.</p>
</blockquote>
<p>Me-remote device atau khususnya server biasa dilakuan oleh programmer untuk melakukan deploy aplikasi atau sekedar untuk mencopy file dari local device ke remote device. Protocol yang digunakan biasanya adalah SSH (Secure Shell) baik untuk mendapat akses shell dari target device atau untuk sekedar mengcopy file. biasa bisa menggunakan FTP (File Transfer Protocol) hanya saja untuk FTP tidak disarankan karena masalah keamanannya. Lebih disarankan menggunakan SFTP yang memanfaatkan protocol SSH untuk mentransfer data karena dinilai lebih aman.</p>
<p>Pada umumnya kita akan me-remote shell ke PC atau server menggunakan cara menjalankan command seperti ini <code>ssh verri@10.2.0.2</code>. Setelah itu terminal akan meminta kita untuk input password. Setelah kita menginput password, maka kita akan mendapatkan shell dari Remote Device. Tapi bagaimana jika kita ingin mengremote server tanpa password dan juga tanpa mengikutkan user account pada command ssh. Saya akan jelaskan caranya pada tulisan ini.</p>
<h2 id="heading-generate-ssh-key-pairs-baru">Generate SSH Key Pairs Baru</h2>
<p>Langkah pertama sebelum meng-config ssh yaitu kita perlu men-generate ssh key pairs dengan menggunakan <code>ssh-keygen</code>. Tapi sebelum itu lebih baik kita membuat direktori baru di folder <code>/home/user/.ssh</code>. Sebagai contoh disini saya menggunakan nama direktory <code>remote</code>.</p>
<pre><code class="lang-bash">$ mkdir ~/.ssh/remote
$ <span class="hljs-built_in">cd</span> ~/.ssh/remote
$ ssh-keygen -t rsa -b 1024 -C verri@ndriawan

Generating public/private ed25519 key pair.
Enter file <span class="hljs-keyword">in</span> <span class="hljs-built_in">which</span> to save the key (/tmp/keys/id_ed25519):
</code></pre>
<p>Pada baris <code>Enter file blablbla</code>, kita diminta untuk menginputkan file path dimana key baru akan distore. Jika pada contoh output di atas, default path akan distore di <code>/tmp/keys</code> dengan nama file <code>id_ed25519</code>. Tapi karena kita sudah membuat folder baru <code>~/.ssh/remote</code> maka kita bisa inputkan lokasi file keys pada folder tersebut seperti ini <code>~/.ssh/remote/id_ed25519</code>. Lalu tekan Enter.</p>
<p>Berikut Informasi options dari command:</p>
<ul>
<li><p><strong>-t</strong> : Type key yang dipilih, secara default, type key akan memilih <code>RSA</code>. tapi pada contoh menggunakan <code>ed25519</code>.</p>
</li>
<li><p><strong>-b</strong>: Panjang bits key, yang dimana pada contoh <strong>1024</strong>. Tapi options ini akan di-ignore atau tidak digunakan untuk type key ras, beda dengan <strong>RSA</strong> yang key lengthnya dinamic dan minimal secure bits-nya adalah 1024.</p>
</li>
<li><p><strong>-C</strong>: adalah comment, saya menambahkan commit <code>verri@ndriawan</code> sebagi penanda bahwa key itu milik si pembuat key.</p>
</li>
</ul>
<p>Selanjutnya akan muncul pada prompt meminta kita untuk meinputkan password. Kita bisa menginputkan password yang kita inginkan. Tapi password ini optional, jadi tidak menginputkan password pun tidak masalah untuk kepentingan yang tidak terlalu memerlukan keamanan. Kurang lebih output pada terminal akan seperti ini.</p>
<pre><code class="lang-tsx">Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in ~/.ssh/remote
Your public key has been saved in ~/.ssh/remote/id_ed25519.pub
The key fingerprint is:
SHA:blabla verri@ndriawan
The key's randomart image is:
+---[RSA 1024]----+
|  ..==o.         |
|=...             |
|o                |
+----[SHA256]-----+
</code></pre>
<p>Pada contoh key yang digenerate di atas, key akan menggunakan <code>id_ed25519</code>. Penamaan ini bebas. Pengguna bisa mengenerate key pairs dengan nama apa saja. Baiknya penamaan sesuaikan dengan kebutuhan saja.</p>
<p>Setelah generate selesai, maka seharusnya akan ada dua file yang terbuat, yaitu file <code>id_ed25519</code> dan file <code>id_</code><a target="_blank" href="http://ed25519.pub"><code>ed25519.pub</code></a>. Content dari file <code>id_</code><a target="_blank" href="http://ed25519.pub"><code>ed25519.pub</code></a> inilah yang akan didistribusikan ke server nantinya.</p>
<h2 id="heading-alur-koneksi-ssh">Alur koneksi SSH</h2>
<p>Ketika membuka koneksi, SSH akan memastikan pada file <code>~/.ssh/config</code> apakah <code>destination</code> host ada di dalam config tersebut atau tidak. Jika tidak, maka ssh akan meminta kita sebagai commander untuk mengikutkan argument-argument pada command yang akan dieksekusi. tetapi jika config tersebut ada, maka ssh akan menggunakan config tersebut. Jika kita buka file <code>~/.ssh/config</code>, isi dari file tersebut kurang lebih ssh config pada sisi client akan terlihat seperti ini.</p>
<pre><code class="lang-bash">Host 10.2.0.2
  User root
  IdentityFile ~/.ssh/id_ed25519
</code></pre>
<p>Jika kita translate config tersebut ke dalam bentuk command, maka kurang lebih akan menjadi seperti ini.</p>
<pre><code class="lang-bash">$ ssh -i ~/.ssh/id_ed25519 root@10.2.0.2
</code></pre>
<p>Dimana option (-i) adalah identity file yang merupakan file <code>private key</code> yang digunakan untuk autentikasi. Seperti itulah alur pada sisi client.</p>
<p>Selanjutnya alur pada sisi server, ketika ada permintaan koneksi dari client dengan menggunakan protocol SSH seperti pada contoh sebelumnya, server akan melakukan verifikasi pair key yang ada pada file <code>~/.ssh/authorized_keys</code> berdasarkan identity file yang diikutkan pada command ssh. Jika pair key dari id tersebut terdaftar pada file <code>authorized_keys</code>, selanjutkan secara sederhananya server dan client akan saling mengautentikasi sehingga akhirnya client mendapatkan shell dari server.</p>
<h2 id="heading-distribusi-key">Distribusi Key</h2>
<p>Setelah memahami cara autentikasi protocol SSH, maka selanjutnya yang perlu kita lakukan adalah mendistribusikan public key yang sudah dibuat sebelumnya ke server dengan menggunakan contoh command dibawah ini.</p>
<pre><code class="lang-bash">$ cat ~/.ssh/id_ed25519.pub | ssh root@10.2.0.2 <span class="hljs-string">"cat - &gt;&gt; /root/.ssh/authorized_keys"</span>
</code></pre>
<p>lalu coba melakukan koneksi ke server menggunakan command ssh seperti ini</p>
<pre><code class="lang-bash">$ ssh -i ~/.ssh/id_ed25519 root@10.2.0.2
</code></pre>
<p>Jika berhasil, maka terminal dimana kita menjalankan command SSH tersebut akan mendapatkan shell dari server. Tapi jika gagal melakukan autentikasi, maka yang perlu dilakukan adalah coba merubah mode folder <code>~/.ssh</code> pada server ke permission <code>700</code> dan juga untuk file <code>~/.ssh/authorized_keys</code> pada server ubah ke mode <code>600</code>. sebagai contoh command seperti berikut</p>
<pre><code class="lang-bash">$ chmod 700 ~/.ssh &amp;&amp; chmod 600 ~/.ssh/authorized_keys
</code></pre>
<p>Selanjutnya coba login ke remote server lagi menggunakan command ssh seperti sebelumnya.</p>
<h2 id="heading-kesimpulan">Kesimpulan</h2>
<p>SSH adalah protocol aman yang digunakan untuk komunikasi antara dua node. SSH menggunakan berbagai algoritma enkripsi untuk mengauentikasi antar node. Selain menggunakan credential dari host untuk mengautentikasi antar node, SSH juga bisa menggunakan pair keys. Sehingga pengguna tidak perlu menginputkan password ketika mengauntentikasi dengan server. Dengan begitu, password akan tetap terjaga karena tidak akan sering ditransfer dari client ke server. Mengurangi resiko MITM atau keylogger.</p>
]]></content:encoded></item><item><title><![CDATA[Intro Blog]]></title><description><![CDATA[Halo, selamat datang di sini. Saya Verri Andriawan seorang Indonesian Full Stack Engineer yang sudah cukup terbiasa dengan berbagai technology dari mulai frontend, backend hingga server. Saya juga melabeli diri saya sebagai seorang Tech Enthusiast ya...]]></description><link>https://verri.andriawan.web.id/intro-blog</link><guid isPermaLink="true">https://verri.andriawan.web.id/intro-blog</guid><category><![CDATA[Intro]]></category><category><![CDATA[profile]]></category><category><![CDATA[indonesia]]></category><category><![CDATA[web3.0]]></category><dc:creator><![CDATA[Andriawan]]></dc:creator><pubDate>Sat, 14 Dec 2024 17:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Halo, selamat datang di sini. Saya Verri Andriawan seorang Indonesian Full Stack Engineer yang sudah cukup terbiasa dengan berbagai technology dari mulai frontend, backend hingga server. Saya juga melabeli diri saya sebagai seorang Tech Enthusiast yang tertarik dengan berbagai perkembangan technology saat ini khususnya seperti Blockchain, AI dan Robotics. Yang paling penting, saya bukan windows user. Sehari-hari saya di office menggunakan Ubuntu dan di luar kantor saya banyak mengguakan OSX. Jadi bisa dipastikan tulisan-tulisan saya di blog ini lebih dominan menggunakan environment Linux.</p>
<p>To be honest, ini bukan blog pertama saya, saya sudah berkali-kali membuat blog hanya saja tidak awet, baik itu blog konyol, blog serius pun blog hybrid. Karena keterbatasan waktu lebih banyak melakukan eksplorasi dan kerja dibanding menulis sehingga membuat blog-blog lama terbengkalai. Jadi pada blog ini saya akan coba menuliskan pengalaman-pengalaman dan hasil eksplore saya.</p>
<p>Pada saat tulisan ini dibuat (15 Desember 2024), saya bekerja dan dipercaya sebagai Lead Software di PT Widya Inovasi Indonesia yang di publik lebih dikenal sebagai Widya Robotics. Perusahaan yang bergerak di bidang Robotic dan AI di Yogyakarta.</p>
<p>So, salam kenal untuk pembaca..</p>
<p>Regards,</p>
<p>Verri Andriawan</p>
]]></content:encoded></item></channel></rss>