Lompat ke konten
dokumentasi.id
Commit 43aad26
v1
Star1,1 rb

2

Arsitektur Berlapis

Template ini membagi kode menjadi empat lapisan: delivery, usecase, repository, dan gateway. Aturan utamanya satu arah — lapisan luar boleh memanggil lapisan dalam, tidak sebaliknya. README menuliskan urutan itu secara eksplisit, dari external system yang mengirim request sampai gateway yang memanggil sistem luar. README.md:7-21

Empat Lapisan dan Perannya

LapisanPaketTanggung jawabIdentifier contoh
Delivery HTTPinternal/delivery/httpmembaca request, memanggil usecase, menulis responseUserController.Register
Delivery messaginginternal/delivery/messagingmembongkar event, meneruskan ke handlerUserConsumer.Consume
Usecaseinternal/usecasevalidasi, batas transaksi, orkestrasiAddressUseCase.Create
Repositoryinternal/repositoryoperasi database lewat entityRepository[T].Create
Gatewayinternal/gateway/messagingmenyusun dan mengirim pesan ke sistem luarProducer[T].Send

Lapisan delivery HTTP menerima request, membentuk model, lalu memanggil usecase. UserController.Register membaca body ke model.RegisterUserRequest, memanggil UseCase.Create, dan membalas model.WebResponse. internal/delivery/http/user_controller.go:24-39 Delivery messaging memegang peran yang sama untuk event: UserConsumer.Consume membongkar JSON menjadi model.UserEvent, lalu mencatat isinya ke log. internal/delivery/messaging/user_consumer.go:21-31

Lapisan usecase memegang aturan bisnis dan batas transaksi. Struct UserUseCase menyimpan DB, Log, Validate, UserRepository, dan UserProducer. internal/usecase/user_usecase.go:19-25 AddressUseCase.Create memperlihatkan pola lengkapnya: validasi request, ambil entity.Contact lewat ContactRepository, bentuk entity.Address, simpan lewat AddressRepository.Create, commit, lalu kirim event lewat AddressProducer.Send. internal/usecase/address_usecase.go:40-87

Lapisan repository hanya berbicara dengan database memakai entity. Repository[T] menyediakan Create, Update, Delete, CountById, dan FindById di atas *gorm.DB. internal/repository/repository.go:5-29

Lapisan gateway menyusun panggilan ke sistem luar. Producer[T model.Event] melakukan json.Marshal pada event lalu mengirim sarama.ProducerMessage ke topic miliknya. internal/gateway/messaging/producer.go:11-42 Tiap producer konkret hanya menetapkan topic: UserProducer memakai topic users. internal/gateway/messaging/user_producer.go:10-21 Kontrak antara usecase dan gateway adalah interface model.Event dengan satu method GetId. internal/model/event.go:3-5

Arah Dependensi

graph TD
  web["cmd/web"] --> config["internal/config"]
  worker["cmd/worker"] --> config
  worker --> dmsg["delivery/messaging"]
  config --> dhttp["delivery/http"]
  config --> dmsg
  config --> uc["usecase"]
  config --> repo["repository"]
  config --> gw["gateway/messaging"]
  dhttp --> uc
  dhttp --> mw["delivery/http/middleware"]
  dhttp --> model["model"]
  mw --> uc
  dmsg --> model
  uc --> repo
  uc --> gw
  uc --> entity["entity"]
  uc --> model
  uc --> conv["model/converter"]
  repo --> entity
  repo --> model
  gw --> model
  conv --> entity
  conv --> model
  style repo fill:#336791,color:#fff
  style entity fill:#336791,color:#fff
  style model fill:#8b5cf6,color:#fff
  style conv fill:#8b5cf6,color:#fff
  style gw fill:#f59e0b,color:#000
  style config fill:#f59e0b,color:#000
%% fig: Arah dependensi antar paket di internal
%% desc: Setiap panah menunjuk paket yang diimpor; delivery memanggil usecase, usecase memanggil repository dan gateway, dan keduanya hanya menyentuh entity serta model.
Fig. 1: Arah dependensi antar paket di internalSetiap panah menunjuk paket yang diimpor; delivery memanggil usecase, usecase memanggil repository dan gateway, dan keduanya hanya menyentuh entity serta model.

Diagram di atas dibaca dari import nyata, bukan dari niat desain. delivery/http mengimpor model, usecase, dan middleware. internal/delivery/http/user_controller.go:3-10 Middleware auth juga memanggil usecase, karena NewAuth menerima *usecase.UserUseCase dan memanggil Verify. internal/delivery/http/middleware/auth_middleware.go:10-25

usecase mengimpor entity, gateway/messaging, model, model/converter, dan repository — lima paket dalam sekaligus. internal/usecase/user_usecase.go:3-17 repository mengimpor entity dan model. internal/repository/contact_repository.go:3-9 gateway/messaging hanya mengimpor model, sehingga tidak tahu apa pun soal database maupun HTTP. internal/gateway/messaging/producer.go:11-42 model/converter mengimpor entity dan model untuk memetakan satu ke yang lain lewat UserToResponse dan UserToEvent. internal/model/converter/user_converter.go:8-29

Entity sebagai Titik Netral

entity tidak mengimpor paket internal lain. Struct User hanya membawa tag gorm yang mengikat setiap field ke kolom tabel users. internal/entity/user_entity.go:1-16 Posisi ini membuat repository dan usecase dapat berbagi bentuk data tanpa saling bergantung pada tipe konkret milik lapisan lain.

model juga hanya berisi struct data. WebResponse[T] dipakai lintas lapisan sebagai amplop response, dengan field Data, Paging, dan Errors. internal/model/model.go:3-19

Penyimpangan dari Aturan

⚠️ Dua ketergantungan keluar dari pakem dan perlu diketahui sebelum menambah kode. ContactRepository mengimpor internal/model karena method Search menerima *model.SearchContactRequest dan memakainya di FilterContact. internal/repository/contact_repository.go:26-38 UserUseCase mengimpor github.com/gofiber/fiber/v2 untuk mengembalikan fiber.ErrBadRequest saat validasi gagal. internal/usecase/user_usecase.go:3-17 Akibatnya usecase tidak lagi sepenuhnya bebas dari framework HTTP: error yang dikembalikannya sudah berupa fiber.Error. Pemakaian itu terlihat di Verify. internal/usecase/user_usecase.go:38-46

Titik Perakitan

Tidak ada lapisan yang membuat lapisan lain sendiri. internal/config mengimpor delivery/http, delivery/http/middleware, delivery/http/route, gateway/messaging, repository, dan usecase sekaligus, lalu merangkainya di satu fungsi Bootstrap. internal/config/app.go:3-17 Urutannya tetap: repository dulu, lalu producer, usecase, controller, middleware, dan terakhir route. internal/config/app.go:28-66 Route mendaftarkan controller ke fiber.App, dengan middleware auth dipasang sebelum grup route terautentikasi. internal/delivery/http/route/route.go:17-25

Dua entrypoint memakai perakitan yang berbeda. cmd/web membuat fiber.App dan sarama.SyncProducer lalu memanggil config.Bootstrap. cmd/web/main.go:8-30 cmd/worker tidak memakai Bootstrap sama sekali; ia menjalankan tiga consumer dan menyerahkan topic serta handler ke messaging.ConsumeTopic. cmd/worker/main.go:43-62 ConsumeTopic menjalankan consumerGroup.Consume di dalam goroutine dan menutup group saat context dibatalkan. internal/delivery/messaging/consumer.go:46-75

Rincian perakitan dan urutan bootstrap ada di Konfigurasi dan Bootstrap. Siklus satu request HTTP dibahas di Alur Request HTTP, dan perjalanan event dibahas di Alur Event Kafka.