Hexagonal Architecture: business logic ও adapter আলাদা রাখা
Layered and Hexagonal Architecture with Dependency Injection
এই পাঠের সূচিপত্র
এই পাঠে যা শিখবেন
- Business policy ও infrastructure আলাদা করুন
- Port adapter ও dependency injection ব্যবহার করুন
- অপ্রয়োজনীয় abstraction এড়ান
সমস্যা দিয়ে শুরু
Checkout calculation controller, Eloquent query ও payment HTTP request-এর সঙ্গে এক method-এ আছে। Unit test চালাতে real gateway credentials দরকার হচ্ছে। Gateway বদলাতে business discount logic-ও স্পর্শ করতে হয়। পরিবর্তনের কারণগুলো আলাদা নয়।
সহজ করে বুঝি
Hexagonal architecture application core-কে ports বা contracts দিয়ে বাইরের adapter থেকে আলাদা করে। Inbound adapter controller বা CLI; outbound adapter database বা payment client। Dependency core-এর দরকারের দিকে যায়, vendor SDK-এর দিকে নয়। Layered architecture-ও responsibility আলাদা করে; ছয়টি folder থাকা বাধ্যতামূলক নয়।
পরিচিত একটি উপমা
একটি electrical appliance socket-এর মান জানে, বিদ্যুৎকেন্দ্রের internal machine নয়। Travel adapter বদলে অন্য দেশে চালানো যায়। তবে প্রতিটি তারের জন্য আলাদা socket বানালে সুবিধার চেয়ে জটিলতা বাড়ে; abstraction-এর কারণ থাকা চাই।
নকশাটি দেখুন
Diagram লোড হচ্ছে…
Diagram-এর text flow দেখুন
flowchart LR H[Laravel Controller] --> U[Checkout Use Case] C[CLI Command] --> U U --> P[Payment Port] P --> A[HTTP Gateway Adapter] U --> R[Order Repository Port] R --> D[Eloquent Adapter]
Controller validated input দিয়ে use case ডাকে। CLI একই business flow চালাতে পারে। Use case payment port জানে; HTTP headers জানে না। Eloquent adapter storage detail বহন করে। Test-এ port-এর fake দিয়ে gateway ছাড়া core rule যাচাই হয়।
PHP ও Laravel-এ প্রয়োগ
PHP interface ও use case fragment। AppServiceProvider-এ PaymentGateway interface বাস্তব adapter-এ bind হবে। Payment provider-এর idempotency contract আলাদা; interface বানানো নিজে network reliability দেয় না। Amount server-calculated integer এবং order authorization আগে হয়েছে।
interface PaymentGateway
{
public function charge(string $operationKey, int $amountMinor): string;
}
final class CollectPayment
{
public function __construct(private PaymentGateway $gateway) {}
public function execute(string $orderKey, int $amountMinor): string
{
if ($amountMinor <= 0) {
throw new InvalidArgumentException('Amount must be positive.');
}
return $this->gateway->charge('order:'.$orderKey, $amountMinor);
}
}
// Service provider: bind PaymentGateway to the HTTP gateway adapter.
// Tests inject a deterministic fake implementing this exact contract.এটি PHP 8.3+ ও Laravel 12-এর শিক্ষামূলক fragment; সম্পূর্ণ application নয়। সংশ্লিষ্ট route-এ authentication ও resource authorization প্রয়োগ করতে হবে। নিচের অনুশীলনে failure ইচ্ছা করে তৈরি করুন; শুধু সফল response দেখে correctness ধরে নেবেন না।
সুবিধা, সীমাবদ্ধতা ও trade-off
Core tests দ্রুত ও adapter replacement সহজ হয়। Interfaces, DTO ও mapping code বাড়ে। সব CRUD model-এ repository wrapper বানালে লাভ নাও হতে পারে; external dependency বা গুরুত্বপূর্ণ business rule-এ boundary দিয়ে শুরু করুন।
সাধারণ ভুল
- Interface একই vendor SDK-এর সব method copy করে dependency উল্টো না করা।
- Controller থেকে business rule adapter-এ সরিয়ে আবার coupling রাখা।
- সব facade নিষিদ্ধ করলেই architecture ভালো ভাবা।
আরও একটু গভীরে
Port-এর failure vocabulary application-এর প্রয়োজন অনুযায়ী হওয়া দরকার: rejected, unavailable ও outcome unknown ভিন্ন। Generic exception সব মিশিয়ে ফেললে business recovery কঠিন হয়। Dependency injection object wiring করে; domain boundary নিজে নির্ধারণ করে না।
Payment adapter ছাড়াই fake gateway দিয়ে positive amount ও invalid amount test করুন। Fake unknown outcome ফেরালে use case কী state রাখবে নির্ধারণ করুন। এরপর বাস্তব adapter contract test একই semantic ফল মেনে চলে কি না যাচাই করুন।
মনে রাখুন
- Policy ও transport-এর কারণ আলাদা।
- Port নিজের use case-এর ভাষা।
- Abstraction প্রয়োজন অনুযায়ী।
নিজে চেষ্টা করুন
Shipping label তৈরির জন্য port design করুন। Vendor timeout এবং definite invalid address যেন ভিন্ন ফল হয়; controller কী response দেবে লিখুন।
নিজের বোঝা যাচাই করুন
1. ছয়টি layer দরকার?
না। Hexagon illustration boundary বোঝায়; নির্দিষ্ট folder বা layer সংখ্যা requirement নয়।
2. Port কী?
Application কোন capability চায় তার contract; vendor-এর পুরো SDK copy নয়।
3. DI কী দেয়?
Implementation inject করতে সাহায্য করে; responsibility এবং failure semantics design করতে হয় আপনাকেই।
4. সব model repository?
প্রয়োজন নেই। Simple CRUD-তে wrapper শুধু অতিরিক্ত indirection দিতে পারে।
5. Fake কি integration test?
না। Core behavior যাচাই হয়; HTTP adapter ও provider contract আলাদা verify করতে হয়।
আরও পড়ুন
এই পাঠের কুইজ
এই পাঠ থেকে ২টি প্রশ্ন। উত্তর দিয়ে ব্যাখ্যা দেখুন; অধ্যায়ের কুইজ আলাদাভাবে দিতে হবে।
এই পাঠের ২টি প্রশ্নের উত্তর দিন। পাস করতে দুটিই সঠিক হতে হবে।
জমা দেওয়ার পর সঠিক উত্তর ও ব্যাখ্যা দেখবেন। ফলাফল এই ব্রাউজারে সংরক্ষিত থাকবে। অনুশীলনের ফল অধ্যায় সম্পন্নের হিসাবে যোগ হয় না।
অগ্রগতি এই ব্রাউজারে সংরক্ষিত থাকে। শুধু পাঠ খুললে সম্পন্ন হয় না।