فصل ۸ — الگوهای معماری
الگوهای تاکتیکی بررسیشده تا اینجا روشهای مختلف مدلسازی و پیادهسازی منطق کسبوکار را تعریف کردند. در این فصل تصمیمهای طراحی تاکتیکی را در زمینهای وسیعتر بررسی میکنیم: روشهای مختلف هماهنگکردن تعاملها و وابستگیهای میان اجزای یک سیستم.
منطق کسبوکار در برابر الگوهای معماری
منطق کسبوکار مهمترین بخش نرمافزار است، اما تنها بخش آن نیست. برای پیادهسازی نیازمندیهای Functional و Nonfunctional، Codebase مسئولیتهای بیشتری دارد: تعامل با کاربران برای دریافت ورودی و ارائهٔ خروجی، استفاده از مکانیزمهای ذخیرهسازی برای Persist کردن State، و یکپارچهشدن با سیستمها و ارائهدهندگان اطلاعات بیرونی.
این تنوع دغدغهها باعث میشود منطق کسبوکار بهراحتی میان اجزای مختلف پراکنده شود؛ بخشی در UI یا Database پیاده شود یا در چند جزء تکرار گردد. اگر دغدغههای پیادهسازی سازماندهی سختگیرانه نداشته باشند، Codebase تغییرپذیری دشواری پیدا میکند. هنگام تغییر منطق کسبوکار معلوم نیست دقیقاً کدام بخشها باید تغییر کنند؛ ممکن است تغییر بر بخشهای ظاهراً نامرتبط اثر جانبی بگذارد یا بخشی که باید اصلاح شود از قلم بیفتد. همهٔ این موارد هزینهٔ نگهداری را بهشدت افزایش میدهند.
الگوهای معماری اصول سازماندهی برای جنبههای مختلف Codebase و مرزهای روشن میان آنها معرفی میکنند: اینکه منطق کسبوکار چگونه به ورودی، خروجی و زیرساخت متصل میشود، اجزا چه دانشی با هم به اشتراک میگذارند و چگونه به یکدیگر Reference میدهند.
انتخاب روش مناسب سازماندهی Codebase یا الگوی معماری درست برای پشتیبانی کوتاهمدت از پیادهسازی منطق و کاهش هزینهٔ نگهداری بلندمدت حیاتی است. سه الگوی عمده را بررسی میکنیم: Layered Architecture، Ports & Adapters و CQRS.
معماری لایهای (Layered Architecture)
معماری لایهای یکی از رایجترین الگوهای معماری است. Codebase را به لایههای افقی تقسیم میکند که هرکدام یک دغدغهٔ فنی را بر عهده دارند: تعامل با مصرفکنندگان، پیادهسازی منطق کسبوکار و Persist کردن داده؛ شکل ۸-۱.
شکل ۸-۱ — معماری لایهای.
در شکل کلاسیک، سه لایه وجود دارد: Presentation Layer (PL)، Business Logic Layer (BLL) و Data Access Layer (DAL).
لایهٔ ارائه (Presentation Layer)
Presentation Layer، رابط کاربری برنامه برای تعامل با مصرفکنندگان را پیادهسازی میکند؛ شکل ۸-۲. در نسخهٔ کلاسیک، منظور رابط گرافیکی مانند Web یا Desktop بود. در سامانههای مدرن دامنهٔ مسئولیت وسیعتر است و تمام راههای Trigger کردن رفتار برنامه، هم Synchronous و هم Asynchronous، را دربر میگیرد:
- Graphical User Interface (GUI)
- Command-Line Interface (CLI)
- API برای یکپارچهسازی برنامهای با سیستمهای دیگر
- Subscription به Eventها در Message Broker
- Message Topic برای انتشار Eventهای خروجی
همهٔ اینها راههای دریافت درخواست از محیط بیرونی و انتقال خروجی هستند. دقیقتر بگوییم، Presentation Layer همان رابط عمومی برنامه است.
شکل ۸-۲ — Presentation Layer.
لایهٔ منطق کسبوکار (Business Logic Layer)
این لایه مسئول پیادهسازی و کپسولهسازی منطق کسبوکار است؛ جایی که تصمیمهای کسبوکاری اجرا میشوند و به تعبیر Eric Evans، قلب نرمافزار است. الگوهای فصلهای ۵ تا ۷، مانند Active Record و Domain Model، در این لایه قرار میگیرند؛ شکل ۸-۳.
شکل ۸-۳ — Business Logic Layer.
لایهٔ دسترسی داده (Data Access Layer)
DAL دسترسی به مکانیزمهای Persistence را فراهم میکند. در نسخهٔ کلاسیک منظور Database بود، اما در سامانههای مدرن مسئولیت گستردهتر شده است.
پس از انقلاب NoSQL، یک سیستم معمولاً با چند Database کار میکند؛ مثلاً Document Store برای Operational Data، Search Index برای Queryهای پویا و In-Memory Database برای عملیات بهینهشده از نظر Performance.
پایگاه داده تنها رسانهٔ ذخیرهسازی نیست؛ Cloud Object Storage میتواند فایلها را نگهدارد و Message Bus میتواند ارتباط میان Functionهای برنامه را هماهنگ کند. این لایه همچنین یکپارچهسازی با ارائهدهندگان اطلاعات بیرونی را شامل میشود: API سیستمهای خارجی یا Managed Serviceهای Cloud مانند ترجمهٔ زبان، دادهٔ بازار سهام و Transcription صوت؛ شکل ۸-۴.
شکل ۸-۴ — Data Access Layer.
ارتباط میان لایهها
لایهها با مدل ارتباطی Top-down یکپارچه میشوند: هر لایه فقط میتواند به لایهٔ مستقیماً پایینتر Dependency داشته باشد؛ شکل ۸-۵. این ساختار دغدغههای پیادهسازی را جدا و دانش مشترک میان لایهها را کم میکند. Presentation Layer فقط BLL را میشناسد و از تصمیمهای DAL بیاطلاع است.
شکل ۸-۵ — ارتباط لایهای.
گونهٔ دارای Service Layer
مرز یک Application را با لایهای از Serviceها تعریف میکند که مجموعهٔ عملیات در دسترس را برقرار و پاسخ Application را در هر عملیات هماهنگ میکند.
Patterns of Enterprise Application Architecture
معماری لایهای اغلب با لایهٔ چهارم، Service Layer، توسعه داده میشود. این لایه میان Presentation و Business Logic قرار میگیرد. کد زیر نمونهای است که Controller در Presentation Layer مستقیماً Active Record را ایجاد میکند و Transaction را هماهنگ میسازد:
namespace MvcApplication.Controllers
{
public class UserController: Controller
{
...
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(ContactDetails contactDetails)
{
OperationResult result = null;
try
{
_db.StartTransaction();
var user = new User();
user.SetContactDetails(contactDetails)
user.Save();
_db.Commit();
result = OperationResult.Success;
} catch (Exception ex) {
_db.Rollback();
result = OperationResult.Exception(ex);
}
return View(result);
}
}
}
برای جداسازی بیشتر Presentation از منطق زیرین، این Orchestration را میتوان به Service Layer منتقل کرد؛ شکل ۸-۶. در این الگو Service Layer یک مرز منطقی است، نه یک سرویس فیزیکی.
شکل ۸-۶ — Service Layer.
Service Layer بهعنوان Façade برای Business Logic عمل میکند: رابطی متناظر با عملیات عمومی سیستم ارائه و هماهنگی لایههای زیرین را کپسوله میکند:
interface CampaignManagementService
{
OperationResult CreateCampaign(CampaignDetails details);
OperationResult Publish(CampaignId id, PublishingSchedule schedule);
OperationResult Deactivate(CampaignId id);
OperationResult AddDisplayLocation(CampaignId id, DisplayLocation newLocation);
...
}
این متدها متناظر با Public Interface هستند، اما جزئیات مربوط به Presentation ندارند. مسئولیت Presentation به آمادهکردن Input برای Service Layer و انتقال Response به Caller محدود میشود.
بازآرایی مثال:
namespace ServiceLayer
{
public class UserService
{
...
public OperationResult Create(ContactDetails contactDetails)
{
OperationResult result = null;
try
{
_db.StartTransaction();
var user = new User();
user.SetContactDetails(contactDetails)
user.Save();
_db.Commit();
result = OperationResult.Success;
} catch (Exception ex) {
_db.Rollback();
result = OperationResult.Exception(ex);
}
return result;
}
...
}
}
namespace MvcApplication.Controllers
{
public class UserController: Controller
{
...
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(ContactDetails contactDetails)
{
var result = _userService.Create(contactDetails);
return View(result);
}
}
}
Service Layer صریح چند مزیت دارد: میتوان همان لایه را برای چند Public Interface مثل GUI و API استفاده کرد و Orchestration تکرار نشود؛ Modularity بهتر میشود؛ Presentation و Business Logic بیشتر Decouple میشوند؛ و Test کردن قابلیتهای کسبوکاری آسانتر میشود.
بااینحال همیشه لازم نیست. وقتی Business Logic خود Transaction Script است، عملاً همان Service Layer است و API دیگری فقط همان Interface را تکرار میکند. اما وقتی الگوی منطق به Orchestration خارجی نیاز دارد، مانند Active Record، Service Layer لازم میشود: Service Layer نقش Transaction Script را اجرا میکند و Active Recordها در BLL هستند.
اصطلاحات
برای معماری لایهای نامهای دیگری نیز میبینید:
- Presentation Layer = User Interface Layer
- Service Layer = Application Layer
- Business Logic Layer = Domain Layer = Model Layer
- Data Access Layer = Infrastructure Layer
نویسنده برای جلوگیری از ابهام اصطلاحات اصلی را به کار میبرد، هرچند «User Interface Layer» و «Infrastructure Layer» مسئولیتهای مدرن را بهتر توصیف میکنند و «Application Layer» نیز با مرز فیزیکی Service اشتباه نمیشود.
چه زمانی از Layered Architecture استفاده کنیم؟
Dependency میان BLL و DAL این معماری را برای Business Logicهایی که با Transaction Script یا Active Record پیاده شدهاند مناسب میکند.
اما Domain Model باید هیچ Dependency یا Knowledge از زیرساخت نداشته باشد. Dependency Top-down معماری لایهای این کار را دشوار میکند. ممکن است، اما الگوی بعدی تناسب بیشتری دارد.
اختیاری: Layer در برابر Tier
Layered Architecture اغلب با N-Tier اشتباه میشود. Layer مرز منطقی و Tier مرز فیزیکی است. همهٔ Layerها معمولاً یک Lifecycle دارند و بهصورت یک واحد Deploy میشوند؛ Tier یک Service، Server یا System مستقل قابل Deploy است.
در شکل ۸-۷، Browser، Reverse Proxy، Web Application و Database Server اجزای فیزیکی مستقلاند. ممکن است روی یک Server یا چند Server اجرا شوند، اما چون مستقل Deploy و Manage میشوند Tier هستند، نه Layer. Layerها مرزهای منطقی داخل Web Application هستند.
شکل ۸-۷ — سامانهٔ N-Tier.
Ports & Adapters
معماری Ports & Adapters کاستیهای Layered Architecture را برطرف میکند و برای منطق پیچیدهتر مناسبتر است. جالب اینکه دو الگو شباهت زیادی دارند؛ میتوان معماری لایهای را قدمبهقدم به Ports & Adapters بازآرایی کرد.
اصطلاحات و یکپارچهسازی دغدغههای فنی
Presentation Layer و Data Access Layer هر دو در اصل تعامل با اجزای خارجیاند: Databaseها، سرویسهای خارجی و UI Frameworkها. این جزئیات فنی منطق کسبوکار نیستند؛ پس همه را در یک Infrastructure Layer متحد میکنیم؛ شکل ۸-۸.
شکل ۸-۸ — ادغام Presentation و Data Access در Infrastructure.
اصل وارونگی وابستگی (Dependency Inversion Principle یا DIP)
DIP میگوید Moduleهای سطح بالا که منطق کسبوکار را پیاده میکنند نباید به Moduleهای سطح پایین وابسته باشند. اما Layered Architecture دقیقاً BLL را به Infrastructure وابسته میکند. برای پیروی از DIP رابطه را معکوس میکنیم؛ شکل ۸-۹.
شکل ۸-۹ — وابستگیهای معکوس.
اکنون Business Logic در مرکز قرار دارد و به هیچ جزء زیرساختی وابسته نیست.
سپس Application Layer بهعنوان Façade رابط عمومی افزوده میشود؛ تمام عملیات سیستم را توصیف و منطق کسبوکار را برای اجرای آنها هماهنگ میکند. نتیجه شکل ۸-۱۰ است.
شکل ۸-۱۰ — لایههای کلاسیک Ports & Adapters.
این معماری Business Logic را از لایههای زیرین مستقل میکند و برای Domain Model و Event-Sourced Domain Model مناسب است.
یکپارچهسازی اجزای زیرساختی
هدف اصلی، Decouple کردن Business Logic از Infrastructure است. بهجای Reference مستقیم به زیرساخت، BLL رابطهای انتزاعی Port تعریف میکند و Infrastructure پیادهسازیهای Concrete آنها، یعنی Adapterها، را برای فناوریهای مختلف میسازد؛ شکل ۸-۱۱.
شکل ۸-۱۱ — Ports & Adapters.
Portهای انتزاعی با Dependency Injection یا Bootstrapping به Adapterهای Concrete Resolve میشوند. نمونه:
namespace App.BusinessLogicLayer
{
public interface IMessaging
{
void Publish(Message payload);
void Subscribe(Message type, Action callback);
}
}
namespace App.Infrastructure.Adapters
{
public class SQSBus: IMessaging { ... }
}
گونهها
Ports & Adapters با نامهای Hexagonal Architecture، Onion Architecture و Clean Architecture نیز شناخته میشود. اصول طراحی، اجزا و روابط اصلی یکساناند و فقط واژگان متفاوت است:
- Application Layer = Service Layer = Use Case Layer
- Business Logic Layer = Domain Layer = Core Layer
گاهی اینها بهاشتباه الگوهای مفهومی متفاوت تلقی میشوند؛ نمونهای دیگر از اهمیت زبان فراگیر.
چه زمانی؟
Decouple شدن Business Logic از همهٔ دغدغههای تکنولوژیک، Ports & Adapters را گزینهای عالی برای Domain Model میکند.
تفکیک مسئولیت Command و Query یا CQRS
الگوی Command-Query Responsibility Segregation در سازماندهی Business Logic و Infrastructure همان اصول Ports & Adapters را دارد، اما در مدیریت داده متفاوت است: اجازه میدهد دادهٔ سیستم در چند مدل Persistشده نمایش داده شود.
مدلسازی چندزبانه (Polyglot Modeling)
در بسیاری از موارد یک مدل واحد نمیتواند همهٔ نیازهای سیستم را پاسخ دهد. مثلاً OLTP و OLAP نمایشهای متفاوتی از داده میخواهند.
دلیل دیگر، Polyglot Persistence است. Database بینقص وجود ندارد؛ هرکدام در مقیاسپذیری، Consistency یا Query Model نقاط قوت و ضعف دارند. بهجای جستوجوی Database کامل، میتوان چند Database برای نیازهای مختلف به کار برد: Document Store برای Operational Data، Column Store برای Analytics/Reporting و Search Engine برای Full-text Search.
CQRS همچنین رابطهٔ نزدیکی با Event Sourcing دارد. در Event-Sourced Model، Query مستقیم فقط برای Eventهای یک Aggregate ممکن است. CQRS با Materialize کردن Projectionها در Databaseهای فیزیکی، Query انعطافپذیر را فراهم میکند. بااینحال CQRS فقط مخصوص Event Sourcing نیست و با سایر الگوهای منطق نیز مفید است.
پیادهسازی
مسئولیت مدلها جدا میشود و دو نوع مدل داریم:
Command Execution Model
یک مدل اختصاصی برای عملیات تغییردهندهٔ State. این مدل Business Logic را اجرا، قواعد را Validation و Invariantها را اجرا میکند. همچنین تنها مدل Strongly Consistent و Source of Truth سیستم است. باید Read State سازگار و Optimistic Concurrency برای Update را پشتیبانی کند.
Read Modelها یا Projectionها
سیستم میتواند هر تعداد مدل لازم برای نمایش داده به کاربر یا ارائهٔ اطلاعات به سیستمهای دیگر تعریف کند. Read Model یک Projection ازپیشمحاسبهشده است و میتواند در Durable Database، Flat File یا In-Memory Cache قرار گیرد.
پیادهسازی درست CQRS اجازه میدهد تمام دادهٔ Projection پاک و از صفر بازسازی شود. این ویژگی همچنین افزودن Projectionهای جدیدی را در آینده ممکن میکند که هنگام طراحی اولیه قابل پیشبینی نبودهاند. Read Modelها فقط خواندنی هستند و عملیات سیستم نباید مستقیماً آنها را تغییر دهند.
Projection کردن Read Modelها
تغییرات Command Model باید به تمام Read Modelها Project شوند؛ شکل ۸-۱۲. این مفهوم شبیه Materialized View در پایگاه دادهٔ رابطهای است: با تغییر جدولهای منبع، Viewهای ازپیشمحاسبهشده نیز باید بهروزرسانی شوند.
شکل ۸-۱۲ — معماری CQRS.
Projection همگام
روش Synchronous از Catch-up Subscription استفاده میکند:
- Projection Engine رکوردهای اضافه/Updateشده پس از آخرین Checkpoint را از OLTP میخواند.
- با آنها Read Modelها را Update/Regenerate میکند.
- Checkpoint آخرین رکورد پردازششده را ذخیره میکند تا Iteration بعدی از آنجا ادامه دهد.
شکلهای ۸-۱۳ و ۸-۱۴ این جریان و Sequence Diagram آن را نشان میدهند.
شکل ۸-۱۳ — Synchronous Projection.
شکل ۸-۱۴ — Catch-up Subscription برای Read Modelها.
Command Model باید برای رکوردهای Insert/Updateشده Checkpoint تولید کند و Storage امکان Query بر اساس Checkpoint داشته باشد. مثلاً SQL Server میتواند از ستون rowversion برای عدد یکتا و افزایشی استفاده کند؛ شکل ۸-۱۵. در Databaseهای فاقد این قابلیت میتوان Counter سفارشی داشت.
Query مبتنی بر Checkpoint باید نتایج سازگار بدهد. اگر آخرین رکورد مقدار ۱۰ دارد، در اجرای بعد هیچ رکورد جدیدی نباید Checkpoint کمتر از ۱۰ داشته باشد، وگرنه Projection Engine آن را از دست میدهد.
شکل ۸-۱۵ — ستون Checkpoint خودکار در Database رابطهای.
مزیت بزرگ روش همگام این است که افزودن Projection جدید و بازسازی Projection موجود ساده است؛ کافی است Checkpoint را صفر کنید تا همهٔ رکوردها دوباره Scan شوند.
Projection ناهمگام
در روش Asynchronous، Command Model همهٔ تغییرات Commitشده را روی Message Bus منتشر میکند. Projection Engineها Subscribe میکنند و Read Modelها را Update میکنند؛ شکل ۸-۱۶.
شکل ۸-۱۶ — Asynchronous Projection.
چالشها
با وجود مزایای ظاهری Scale و Performance، روش ناهمگام در برابر مشکلات Distributed Computing حساستر است. پردازش Out-of-order یا Duplicate پیامها میتواند Read Model ناسازگار بسازد و افزودن یا بازسازی Projection نیز دشوارتر است. به همین دلیل پیشنهاد میشود همیشه Synchronous Projection وجود داشته باشد و در صورت نیاز Async بهعنوان مسیر اضافی روی آن قرار گیرد.
تفکیک مدلها
در CQRS، Command فقط روی Strongly Consistent Command Model عمل میکند. Query نباید هیچ Persisted Stateای را مستقیماً تغییر دهد، نه Read Model و نه Command Model.
یک سوءبرداشت رایج این است که Command فقط باید داده را تغییر دهد و هرگز دادهای Return نکند. این تصور اشتباه است و پیچیدگی تصادفی و UX بد ایجاد میکند. Command باید موفقیت یا شکست و دلیل شکست را به Caller بگوید و میتواند ــ و اغلب باید ــ داده Return کند، مثلاً State نتیجهٔ Action برای Update فوری UI. این داده میتواند در Workflow مصرفکننده نیز استفاده شود و Round-trip اضافی را حذف کند.
تنها محدودیت این است که دادهٔ برگشتی باید از Strongly Consistent Command Model بیاید، چون Projectionها Eventually Consistent هستند و نمیتوان انتظار Update فوری آنها را داشت.
چه زمانی از CQRS استفاده کنیم؟
CQRS برای Applicationهایی مناسب است که باید همان داده را در چند مدل، شاید در انواع متفاوت Database، استفاده کنند. از دید دامنه، با ارزش DDD یعنی انتخاب مؤثرترین مدل برای کار همراستاست و امکان بهبود مستمر مدل را میدهد. از دید زیرساخت، میتوان مزیت Databaseهای مختلف را ترکیب کرد: Relational برای Command، Search Index برای Full-text و Flat File ازپیشرندرشده برای Read سریع، در حالی که همهٔ Storageها همگام نگه داشته میشوند.
CQRS همچنین بهطور طبیعی با Event-Sourced Domain Model سازگار است، چون State را به Databaseهای Queryable Project میکند.
دامنهٔ کاربرد الگوها
Layered Architecture، Ports & Adapters و CQRS را نباید الزاماً اصول سازماندهی کل سیستم یا حتی کل Bounded Context دانست. یک Bounded Context ممکن است چند زیردامنهٔ هستهای، پشتیبان و عمومی داشته باشد و حتی زیردامنههای همنوع ممکن است الگوهای Business Logic و Architecture متفاوتی بخواهند. تحمیل یک معماری واحد به کل Context به پیچیدگی تصادفی منجر میشود؛ شکل ۸-۱۷.
شکل ۸-۱۷ — Bounded Context شامل چند Subdomain.
هدف این است که تصمیم طراحی از نیاز واقعی و راهبرد کسبوکار بیاید. در کنار تقسیم افقی Layerها، میتوان تقسیم عمودی ایجاد کرد و برای Moduleهای زیردامنهٔ مختلف مرز منطقی مشخص و ابزار مناسب هرکدام را انتخاب کرد؛ شکل ۸-۱۸.
مرزهای عمودی مناسب یک Monolithic Bounded Context را به Modular Monolith تبدیل میکنند و از Big Ball of Mud جلوگیری میکنند. فصل ۱۱ نشان میدهد این مرزهای منطقی بعداً میتوانند به مرزهای فیزیکی Bounded Contextهای ریزتر تبدیل شوند.
شکل ۸-۱۸ — برشهای معماری (Architectural Slices).
جمعبندی
Layered Architecture، Codebase را بر اساس دغدغههای تکنولوژیک تقسیم میکند. چون Business Logic را به Data Access وابسته میکند، برای سیستمهای Active Record مناسب است.
Ports & Adapters رابطه را معکوس میکند: Business Logic را در مرکز قرار میدهد و آن را از تمام Infrastructure جدا میکند؛ بنابراین با Domain Model سازگار است.
CQRS همان داده را در چند مدل نمایش میدهد. برای Event-Sourced Domain Model عملاً ضروری است، اما هر سیستمی که به چند Persistent Model نیاز دارد میتواند از آن بهره ببرد.
فصل بعد از زاویهای دیگر به Architecture نگاه میکند: چگونه تعامل قابل اتکا میان اجزای مختلف سیستم پیادهسازی شود.
تمرینها
- کدام الگوی معماری با Business Logic پیادهشده با Active Record قابل استفاده است؟
- Layered Architecture
- Ports & Adapters
- CQRS
- A و C
- کدام الگو Business Logic را از Infrastructure جدا میکند؟
- Layered Architecture
- Ports & Adapters
- CQRS
- B و C
- در Ports & Adapters، یکپارچهسازی با Managed Message Bus یک Cloud Provider در کدام Layer قرار میگیرد؟
- Business Logic
- Application
- Infrastructure
- هر لایه
- کدام عبارت دربارهٔ CQRS درست است؟
- Asynchronous Projection آسانتر Scale میشود.
- فقط یکی از Synchronous یا Asynchronous ممکن است، نه هر دو.
- Command نباید هیچ دادهای Return کند.
- Command میتواند داده Return کند به شرطی که از Strongly Consistent Model بیاید.
- A و D.
- CQRS اجازه میدهد همان Business Object در چند Persistent Model و در یک Bounded Context نمایش داده شود. آیا این با مفهوم Bounded Context بهعنوان مرز مدل تناقض دارد؟
شکلها و جدولهای منبع
برای حفظ نمایش بصری دقیق عناصر گرافیکی، بازنمایی صفحههای دارای شکل یا جدول منبع در ادامه آمده است. متن آموزشی فصل در بخشهای بالا به فارسی ترجمه شده است.
بازنمایی صفحهٔ 144 منبع برای حفظ دقیق شکل/جدول اصلی. Figure 8-1. Layered architecture
بازنمایی صفحهٔ 145 منبع برای حفظ دقیق شکل/جدول اصلی. Figure 8-2. Presentation layer | Figure 8-3. Business logic layer
بازنمایی صفحهٔ 146 منبع برای حفظ دقیق شکل/جدول اصلی. Figure 8-4. Data access layer | Figure 8-5. Layered architecture
بازنمایی صفحهٔ 148 منبع برای حفظ دقیق شکل/جدول اصلی. Figure 8-6. Service layer
بازنمایی صفحهٔ 151 منبع برای حفظ دقیق شکل/جدول اصلی. Figure 8-7. N-Tier system
بازنمایی صفحهٔ 152 منبع برای حفظ دقیق شکل/جدول اصلی. Figure 8-8. Presentation and data access layers combined into an infrastructure layer | Figure 8-9. Reversed dependencies
بازنمایی صفحهٔ 153 منبع برای حفظ دقیق شکل/جدول اصلی. Figure 8-10. Traditional layers of the ports & adapters architecture | Figure 8-11. Ports & adapters architecture
بازنمایی صفحهٔ 156 منبع برای حفظ دقیق شکل/جدول اصلی. Figure 8-12. CQRS architecture
بازنمایی صفحهٔ 157 منبع برای حفظ دقیق شکل/جدول اصلی. Figure 8-14. | Figure 8-13. Synchronous projection model | Figure 8-14. Synchronous projection of read models through catch-up subscription
بازنمایی صفحهٔ 158 منبع برای حفظ دقیق شکل/جدول اصلی. Figure 8-15. Auto-generated checkpoint column in a relational database | Figure 8-16. | Figure 8-16. Asynchronous projection of read models
بازنمایی صفحهٔ 160 منبع برای حفظ دقیق شکل/جدول اصلی. Figure 8-17. The subdomains can be of different types: core, supporting, or generic. | Figure 8-17. Bounded contexts spanning multiple subdomains
بازنمایی صفحهٔ 161 منبع برای حفظ دقیق شکل/جدول اصلی. Figure 8-18. Architectural slices