Apa itu Arsitektur Heksagonal
Arsitektur Heksagonal, juga dikenal sebagai pola Ports and Adapters, adalah sebuah pola arsitektur perangkat lunak yang mendorong pemisahan kepentingan dengan memandang aplikasi sebagai heksagon. Setiap sisi heksagon mewakili sebuah port yang berinteraksi dengan dunia eksternal melalui adapter.
Arsitektur Heksagonal bertujuan untuk memusatkan logika bisnis inti aplikasi dan mengisolasi logika tersebut dari kepentingan periferal, menjaga batas yang jelas yang tidak dapat dilanggar oleh faktor eksternal. Isolasi ini memungkinkan perubahan pada satu sisi aplikasi (misalnya, database atau antarmuka pengguna) dapat terjadi secara independen dari sisi lainnya, menjamin kestabilan aplikasi dan mempermudah proses pengujian dan pemeliharaan.
Prinsip-prinsip Arsitektur Heksagonal
Dalam bab ini, saya akan menggali prinsip-prinsip yang mendasari arsitektur heksagonal. Kita akan menjelajahi konsep-konsep kunci seperti aplikasi, port, adapter, dan lapisan heksagonal.
Hexagonal Architecture Explained
Pusat: Aplikasi
Di jantung arsitektur heksagonal terdapat aplikasi, juga dikenal sebagai domain. Ini menggabungkan aturan bisnis dan logika inti perangkat lunak. Aplikasi tidak memiliki ketergantungan pada kepentingan eksternal, seperti database, antarmuka pengguna, atau layanan pihak ketiga apa pun. Fitur ini sangat penting karena memungkinkan kita untuk melakukan perubahan pada bagian sistem tanpa memengaruhi aplikasi inti.
Port: Masuk dan Keluar
Aplikasi berinteraksi dengan dunia luar melalui port, yang dapat dikategorikan menjadi port primer (atau penggerak) dan port sekunder (atau yang digerakkan).
Port primer adalah antarmuka yang mengungkapkan operasi yang disediakan oleh aplikasi kepada dunia luar. Mereka mewakili kasus penggunaan yang didukung oleh aplikasi, mendefinisikan apa yang dapat dilakukan aplikasi. Biasanya, ini adalah API yang akan dirancang dan diimplementasikan oleh pengembang aplikasi.
Port sekunder, di sisi lain, adalah antarmuka yang digunakan aplikasi untuk berinteraksi dengan layanan atau komponen eksternal. Ini adalah hal-hal yang dibutuhkan aplikasi dari dunia luar untuk berfungsi, seperti mekanisme penyimpanan, pemberitahuan, atau API eksternal apa pun.
Adapter: Perantara
Sementara port mendefinisikan jenis interaksi yang mungkin, adapter menangani interaksi tersebut. Adapter adalah penghubung antara aplikasi dan dunia luar. Mereka mengonversi data dari format yang digunakan oleh dunia luar menjadi format yang dapat dipahami oleh aplikasi, dan sebaliknya. Ada beberapa jenis adapter untuk berbagai jenis input dan output, seperti adapter web untuk menangani permintaan HTTP atau adapter database untuk berinteraksi dengan database.
Lapisan Heksagonal
Setiap sisi heksagon dalam arsitektur mewakili sebuah port. Adapter yang berinteraksi dengan port-port ini ditempatkan di tepi heksagon. Penyusunan ini menghasilkan representasi fisik arsitektur sebagai heksagon, yang memberikan nama pola ini.
Arsitektur heksagonal berbeda dari arsitektur berlapis tradisional karena tidak ada lapisan "atas" atau "bawah". Sebaliknya, semua masukan dan keluaran diperlakukan sebagai sama dan ditempatkan di sekitar tepi heksagon, memastikan aplikasi inti tetap terisolasi dan tidak terpengaruh oleh perubahan eksternal.
Implementasi Praktis Arsitektur Heksagonal
Aplikasi berbasis Python yang menggunakan arsitektur heksagonal dapat diorganisir dalam struktur direktori sebagai berikut:
/bookstore
/application_core
__init__.py
application_core.py
/ports_and_adapters
__init__.py
ports_and_adapters.py
/interfaces
__init__.py
interfaces.py
/tests
__init__.py
tests.py
main.py
Merancang Inti Aplikasi
Inti aplikasi menggabungkan aturan bisnis dan logika sistem. Pertimbangkan sebuah aplikasi toko buku yang disederhanakan, di mana kita akan memiliki entitas Book
dan layanan Inventory
.
class Book:
def __init__(self, id, title, author, price):
self.id = id
self.title = title
self.author = author
self.price = price
class Inventory:
def __init__(self, repository):
self.repository = repository
def add_book(self, book):
return self.repository.save(book)
Membuat Port dan Adapter
Di sini kita membuat port dan adapter untuk sistem. Kita mendefinisikan antarmuka BookRepository
(port sekunder) dan implementasi konkret dari antarmuka tersebut, yaitu InMemoryBookRepository
(adapter).
from abc import ABC, abstractmethod
from application_core.application_core import Book
class BookRepository(ABC):
@abstractmethod
def save(self, book: Book):
pass
class InMemoryBookRepository(BookRepository):
def __init__(self):
self.books = {}
def save(self, book: Book):
self.books[book.id] = book
return book
Mengembangkan Antarmuka
Antarmuka mewakili port primer dalam arsitektur heksagonal. Pada aplikasi toko buku kita, mungkin kita akan memiliki antarmuka baris perintah yang sederhana.
from application_core.application_core import Book, Inventory
from ports_and_adapters.ports_and_adapters import InMemoryBookRepository
def command_line_interface():
repository = InMemoryBookRepository()
inventory = Inventory(repository)
book_id = input("Enter book id: ")
title = input("Enter book title: ")
author = input("Enter book author: ")
price = float(input("Enter book price: "))
book = Book(book_id, title, author, price)
inventory.add_book(book)
print(f"Added book {title} to inventory.")
Mengimplementasikan Pengujian
Pengujian lebih mudah ditulis dan dipelihara dalam arsitektur heksagonal karena dependensi dapat digantikan dengan test double. Berikut adalah contoh kasus pengujian sederhana untuk menambahkan buku ke inventaris.
import unittest
from application_core.application_core import Book, Inventory
from ports_and_adapters.ports_and_adapters import InMemoryBookRepository
class TestInventory(unittest.TestCase):
def test_add_book(self):
repository = InMemoryBookRepository()
inventory = Inventory(repository)
book = Book('1', 'Test Book', 'Test Author', 9.99)
inventory.add_book(book)
self.assertEqual(repository.books['1'], book)
if __name__ == '__main__':
unittest.main()
Di direktori utama, Anda mungkin memiliki file main.py
yang mungkin terlihat seperti ini:
from interfaces.interfaces import command_line_interface
if __name__ == '__main__':
command_line_interface()
Referensi