<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Clean Architecture on kenji.blog</title><link>http://kenji.blog/ar/tags/clean-architecture/</link><description>Recent content in Clean Architecture on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>ar</language><copyright>kenjinote</copyright><lastBuildDate>Sat, 12 Sep 2026 12:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/ar/tags/clean-architecture/index.xml" rel="self" type="application/rss+xml"/><item><title>أساسيات هندسة البرمجيات: ما هي البنية النظيفة (Clean Architecture)؟</title><link>http://kenji.blog/ar/p/clean-architecture-basics-guide/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/ar/p/clean-architecture-basics-guide/</guid><description>&lt;img src="http://kenji.blog/p/clean-architecture-basics-guide/img/eyecatch.jpg" alt="Featured image of post أساسيات هندسة البرمجيات: ما هي البنية النظيفة (Clean Architecture)؟" />&lt;p>في تطوير البرمجيات الحديثة، يعد بناء &amp;ldquo;نظام مقاوم للتغيير&amp;rdquo; تحديًا دائمًا. التغييرات في متطلبات العمل، ظهور أطر عمل جديدة، تجديد واجهة المستخدم، وترحيل قواعد البيانات. استجابةً لكل هذه التغييرات، هناك حاجة إلى بنية يمكنها التكيف بمرونة دون إعادة بناء النظام بأكمله. كأحد الإجابات على ذلك، تم اقتراح &lt;strong>البنية النظيفة (Clean Architecture)&lt;/strong> بواسطة Robert C. Martin (المعروف باسم العم بوب، Uncle Bob).&lt;/p>
&lt;p>في هذه المقالة، سنتعمق في جوهر البنية النظيفة من خلال تاريخها، الغرض منها، تفاصيل الطبقات الأربع، قاعدة التبعية، وأمثلة تنفيذية محددة. سنقدم شرحًا تقنيًا عميقًا ومفصلاً للغاية.&lt;/p>
&lt;h2 id="1-مشاكل-البنى-التقليدية-وتاريخ-البنية-النظيفة">1. مشاكل البنى التقليدية وتاريخ البنية النظيفة
&lt;/h2>&lt;p>من الناحية التاريخية، شهدت هندسة البرمجيات تحولات نموذجية مختلفة. في الأنظمة المبكرة، كانت تعليمات منطق العمل، واجهة المستخدم، ووصول البيانات متشابكة بشدة (ما يسمى بشفرة السباغيتي). بعد ذلك، بهدف فصل الاهتمامات (Separation of Concerns)، انتشرت البنية ثلاثية الطبقات (طبقة العرض، طبقة منطق العمل، وطبقة الوصول إلى البيانات).&lt;/p>
&lt;p>ومع ذلك، كان هناك مشكلة رئيسية في البنية التقليدية ثلاثية الطبقات. وهي أن &amp;ldquo;&lt;strong>المجال (منطق العمل) يعتمد على قاعدة البيانات وأطر العمل&lt;/strong>&amp;rdquo;.&lt;/p>
&lt;p>على سبيل المثال، إذا استدعت طبقة منطق العمل طبقة الوصول إلى البيانات (مثل ORM) مباشرةً، فإن التغييرات في مخطط قاعدة البيانات أو التغييرات في ORM ستمتد إلى منطق العمل. بعبارة أخرى، نشأ تناقض حيث أصبحت &amp;ldquo;قواعد العمل&amp;rdquo;، وهي الأكثر أهمية والتي لا ينبغي تغييرها، تعتمد على &amp;ldquo;البنية التحتية&amp;rdquo;، وهي الأكثر عرضة للتغييرات التقنية.&lt;/p>
&lt;p>كحلول لهذه المشكلة، تم ابتكار البنى التالية:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>البنية السداسية (Ports and Adapters)&lt;/strong> - Alistair Cockburn&lt;/li>
&lt;li>&lt;strong>بنية البصلة (Onion Architecture)&lt;/strong> - Jeffrey Palermo&lt;/li>
&lt;li>&lt;strong>DCI (Data, Context and Interaction)&lt;/strong> - James Coplien, Trygve Reenskaug&lt;/li>
&lt;li>&lt;strong>BCE (Boundary-Control-Entity)&lt;/strong> - Ivar Jacobson&lt;/li>
&lt;/ul>
&lt;p>كل هذه البنى لها نفس الغرض. ألا وهو &amp;ldquo;&lt;strong>فصل الاهتمامات&lt;/strong>&amp;rdquo;. يتمثل ذلك في تقسيم البرنامج إلى طبقات، بحيث يمكن اختبار كل منها بشكل مستقل، وتكون مستقلة عن الوكلاء الخارجيين (واجهة المستخدم، قاعدة البيانات، أطر العمل).&lt;/p>
&lt;p>قام Robert C. Martin بدمج مفاهيم هذه البنى الممتازة وتلخيصها في قاعدة عملية واحدة أسماها &amp;ldquo;&lt;strong>البنية النظيفة (Clean Architecture)&lt;/strong>&amp;rdquo;.&lt;/p>
&lt;h2 id="2-الغرض-من-البنية-النظيفة-وخصائصها">2. الغرض من البنية النظيفة وخصائصها
&lt;/h2>&lt;p>النظام الذي يعتمد البنية النظيفة يتمتع بالخصائص التالية:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>الاستقلالية عن أطر العمل (Independent of Frameworks)&lt;/strong>: لا تعتمد البنية على وجود مكتبات برمجية غنية بالميزات. يتيح ذلك استخدام أطر العمل كـ &amp;ldquo;أدوات&amp;rdquo;، دون الحاجة إلى إجبار النظام على التوافق مع قيود إطار العمل.&lt;/li>
&lt;li>&lt;strong>قابلية الاختبار (Testable)&lt;/strong>: يمكن اختبار قواعد العمل دون واجهة المستخدم، قاعدة البيانات، خادم الويب، أو أي عنصر خارجي آخر.&lt;/li>
&lt;li>&lt;strong>الاستقلالية عن واجهة المستخدم (Independent of UI)&lt;/strong>: يمكن تغيير واجهة المستخدم بسهولة دون تغيير بقية النظام. على سبيل المثال، يمكن استبدال واجهة مستخدم الويب بواجهة مستخدم تعتمد على وحدة التحكم (Console UI) دون تغيير قواعد العمل.&lt;/li>
&lt;li>&lt;strong>الاستقلالية عن قاعدة البيانات (Independent of Database)&lt;/strong>: يمكنك تغيير Oracle أو SQL Server إلى Mongo أو BigTable أو CouchDB وما إلى ذلك. قواعد العمل ليست مقيدة بقاعدة البيانات.&lt;/li>
&lt;li>&lt;strong>الاستقلالية عن أي وكيل خارجي (Independent of any external agency)&lt;/strong>: في الواقع، قواعد العمل لا تعرف شيئًا عن العالم الخارجي.&lt;/li>
&lt;/ol>
&lt;h2 id="3-الطبقات-الأربع-للبنية-النظيفة-layers">3. الطبقات الأربع للبنية النظيفة (Layers)
&lt;/h2>&lt;p>تُمثل البنية النظيفة عمومًا برسم بياني لدوائر متحدة المركز. كلما اقتربت من المركز، أصبحت البرمجيات سياسات عالية المستوى (قواعد عمل عالية التجريد). وكلما اتجهت نحو الخارج، أصبحت آليات (تفاصيل محددة).&lt;/p>
&lt;pre class="mermaid">
graph TD
subgraph &amp;#34;External Interfaces (Frameworks &amp;amp; Drivers)&amp;#34;
A[&amp;#34;الويب&amp;#34;]
B[&amp;#34;واجهة المستخدم&amp;#34;]
C[&amp;#34;قاعدة البيانات&amp;#34;]
D[&amp;#34;واجهات برمجة التطبيقات الخارجية&amp;#34;]
end
subgraph &amp;#34;Interface Adapters&amp;#34;
E[&amp;#34;وحدات التحكم&amp;#34;]
F[&amp;#34;البوابات&amp;#34;]
G[&amp;#34;مقدمو العرض&amp;#34;]
end
subgraph &amp;#34;Application Business Rules (Use Cases)&amp;#34;
H[&amp;#34;متفاعلات حالات الاستخدام&amp;#34;]
end
subgraph &amp;#34;Enterprise Business Rules (Entities)&amp;#34;
I[&amp;#34;الكيانات&amp;#34;]
end
A --&amp;gt; E
B --&amp;gt; E
C --&amp;gt; F
D --&amp;gt; F
E --&amp;gt; H
F --&amp;gt; H
G --&amp;gt; H
H --&amp;gt; I
&lt;/pre>
&lt;h3 id="31-الكيانات-entities">3.1. الكيانات (Entities)
&lt;/h3>&lt;p>تغلف الكيانات قواعد العمل على مستوى المؤسسة (Enterprise Business Rules). يمكن أن يكون الكيان كائنًا يحتوي على طرق، أو يمكن أن يكون مجموعة من هياكل البيانات والدوال. إنها القواعد الأكثر عمومية وعالية المستوى والتي يمكن إعادة استخدامها في تطبيقات مختلفة متعددة داخل الشركة.
حتى لو كنت تنشئ تطبيقًا واحدًا فقط، فإن الكيانات هي كائنات العمل لهذا التطبيق. الكيانات لا تتأثر أبدًا بالتغييرات الخارجية (مثل التغييرات في تنقل الصفحات أو التغييرات في الأمان).&lt;/p>
&lt;h3 id="32-حالات-الاستخدام-use-cases">3.2. حالات الاستخدام (Use Cases)
&lt;/h3>&lt;p>تحتوي طبقة حالات الاستخدام على قواعد العمل الخاصة بالتطبيق (Application Business Rules). هنا يتم تغليف وتنفيذ جميع حالات الاستخدام الخاصة بالنظام. تنسق حالات الاستخدام تدفق البيانات من وإلى الكيانات وتوجه الكيانات لتحقيق أهداف النظام.
يجب ألا تؤثر التغييرات في هذه الطبقة على الكيانات. كما أن التغييرات الخارجية مثل قاعدة البيانات، واجهة المستخدم، وأطر العمل لا تؤثر على هذه الطبقة. حالات الاستخدام معزولة تمامًا عن هذه الاهتمامات.&lt;/p>
&lt;h3 id="33-محولات-الواجهات-interface-adapters">3.3. محولات الواجهات (Interface Adapters)
&lt;/h3>&lt;p>طبقة محولات الواجهات عبارة عن مجموعة من المحولات التي تقوم بتحويل البيانات من التنسيق المناسب لحالات الاستخدام والكيانات، إلى التنسيق المناسب للوكلاء الخارجيين مثل قاعدة البيانات والويب.
على سبيل المثال، تنتمي عناصر بنية MVC (نموذج-عرض-تحكم) الخاصة بـ GUI في عالم الويب إلى هنا. تتلقى وحدة التحكم إدخال المستخدم، وتمرره إلى حالة الاستخدام، ويتلقى مقدم العرض المخرجات من حالة الاستخدام ويقوم بتنسيقها للعرض (واجهة المستخدم).
كما أن دور هذه الطبقة هو تحويل البيانات إلى التنسيق الذي يمكن لقاعدة البيانات (مثل SQL) فهمه. يجب ألا يعرف الكود الموجود داخل هذه الطبقة أي شيء عن قاعدة البيانات.&lt;/p>
&lt;h3 id="34-أطر-العمل-والمحركات-frameworks--drivers">3.4. أطر العمل والمحركات (Frameworks &amp;amp; Drivers)
&lt;/h3>&lt;p>تتكون الطبقة الخارجية من أدوات مثل قواعد البيانات وأطر عمل الويب. هنا عادةً لا تكتب الكثير من الكود بخلاف &amp;ldquo;كود الربط&amp;rdquo; للتواصل مع الدوائر الداخلية.
تحتفظ هذه الطبقة بجميع التفاصيل. الويب هو تفصيل. قاعدة البيانات هي تفصيل. نضع هذه التفاصيل في الخارج لتقليل الضرر إلى أدنى حد.&lt;/p>
&lt;h2 id="4-قاعدة-التبعية-the-dependency-rule">4. قاعدة التبعية (The Dependency Rule)
&lt;/h2>&lt;p>لجعل البنية النظيفة تعمل، هناك قاعدة واحدة بالغة الأهمية يجب عدم كسرها أبدًا. وهي &amp;ldquo;&lt;strong>قاعدة التبعية (The Dependency Rule)&lt;/strong>&amp;rdquo;.&lt;/p>
&lt;blockquote>
&lt;p>يجب أن تتجه تبعيات الكود المصدري دائمًا نحو الداخل (نحو السياسات عالية المستوى).&lt;/p>
&lt;/blockquote>
&lt;p>يجب ألا يعرف الكود الذي ينتمي إلى الدوائر الداخلية أي شيء عن الكود الذي ينتمي إلى الدوائر الخارجية. يجب عدم الإشارة إلى الأسماء (الدوال، الفئات، المتغيرات، إلخ) المعلنة في الدوائر الخارجية داخل الدوائر الداخلية.
وبالمثل، يجب عدم استخدام تنسيقات البيانات المستخدمة في الدوائر الخارجية داخل الدوائر الداخلية. وينطبق هذا بشكل خاص إذا كان هذا التنسيق ناتجًا عن إطار عمل في الدائرة الخارجية.&lt;/p>
&lt;pre class="mermaid">
graph LR
A[&amp;#34;أطر العمل والمحركات&amp;#34;] --&amp;gt;| اعتماد | B[&amp;#34;محولات الواجهات&amp;#34;]
B --&amp;gt;| اعتماد | C[&amp;#34;حالات الاستخدام&amp;#34;]
C --&amp;gt;| اعتماد | D[&amp;#34;الكيانات&amp;#34;]
style A fill:#f9f9f9,stroke:#333,stroke-width:2px
style B fill:#e6f7ff,stroke:#333,stroke-width:2px
style C fill:#fff0f6,stroke:#333,stroke-width:2px
style D fill:#f6ffed,stroke:#333,stroke-width:2px
&lt;/pre>
&lt;p>للتعبير عن ذلك رياضيًا، إذا كان مؤشر الطبقة هو $L_i$، وحددنا $i=0$ للكيانات (الطبقة الأعمق) و $i=3$ لأطر العمل (الطبقة الأبعد)، ففي حال وجود تبعية من طبقة $L_m$ إلى $L_n$، يجب أن تتحقق المتباينة التالية دائمًا:&lt;/p>
$$ m > n $$&lt;p>بمعنى آخر، يتجه متجه التبعية $\vec{D}$ دائمًا نحو المركز.&lt;/p>
&lt;h2 id="5-عبور-الحدود-مبدأ-انعكاس-التبعية-dip">5. عبور الحدود: مبدأ انعكاس التبعية (DIP)
&lt;/h2>&lt;p>عند محاولة الالتزام بقاعدة التبعية، ستواجه سريعًا مشكلة كبيرة واحدة. &amp;ldquo;&lt;strong>ماذا تفعل عندما تحتاج حالة الاستخدام إلى جلب بيانات من قاعدة البيانات؟&lt;/strong>&amp;rdquo;&lt;/p>
&lt;p>إذا استدعت طبقة حالات الاستخدام (الداخلية) طبقة محولات الواجهات (تنفيذ المستودع الخارجي) بشكل مباشر، فإن التبعية ستتجه نحو الخارج، مما يشكل انتهاكًا لقاعدة التبعية.&lt;/p>
&lt;p>ما يحل هذه المشكلة هو الحرف &amp;ldquo;D&amp;rdquo; في مبادئ SOLID (مبدأ انعكاس التبعية: Dependency Inversion Principle).&lt;/p>
&lt;h3 id="تعريف-مبدأ-انعكاس-التبعية-dip">تعريف مبدأ انعكاس التبعية (DIP)
&lt;/h3>&lt;ol>
&lt;li>يجب ألا تعتمد الوحدات عالية المستوى على الوحدات منخفضة المستوى. يجب أن يعتمد كلاهما على التجريدات.&lt;/li>
&lt;li>يجب ألا تعتمد التجريدات على التفاصيل. يجب أن تعتمد التفاصيل على التجريدات.&lt;/li>
&lt;/ol>
&lt;p>لتحقيق ذلك، نُعرّف &lt;strong>واجهة (تجريد)&lt;/strong> في طبقة حالات الاستخدام، ونقوم بـ &lt;strong>تنفيذ&lt;/strong> تلك الواجهة في الطبقة الخارجية (محولات الواجهات). تعتمد طبقة حالات الاستخدام فقط على الواجهة التي حددتها، ولا تعتمد على التنفيذ الملموس في الخارج.&lt;/p>
&lt;pre class="mermaid">
classDiagram
class UseCaseInteractor {
- UserRepository repository
+ execute()
}
class UserRepository {
&amp;lt;&amp;lt;Interface&amp;gt;&amp;gt;
+ findById(id)
+ save(user)
}
class UserRepositoryImpl {
+ findById(id)
+ save(user)
}
UseCaseInteractor --&amp;gt; UserRepository : &amp;#34;Depends on (يعتمد على)&amp;#34;
UserRepositoryImpl ..|&amp;gt; UserRepository : &amp;#34;Implements (ينفذ)&amp;#34;
note for UseCaseInteractor &amp;#34;الطبقة الداخلية (حالة الاستخدام)&amp;#34;
note for UserRepository &amp;#34;الطبقة الداخلية (حالة الاستخدام)&amp;#34;
note for UserRepositoryImpl &amp;#34;الطبقة الخارجية (محولات الواجهات)&amp;#34;
&lt;/pre>
&lt;p>في الرسم البياني أعلاه، يكون تدفق التحكم (Control Flow) أثناء وقت التشغيل هو &lt;code>UseCaseInteractor&lt;/code> $\rightarrow$ &lt;code>UserRepositoryImpl&lt;/code>. ومع ذلك، فإن تبعية الكود المصدري (Source Code Dependency) هي &lt;code>UserRepositoryImpl&lt;/code> $\rightarrow$ &lt;code>UserRepository&lt;/code> (نحو الداخل). باستخدام تعدد الأشكال (Polymorphism)، تمكنا من توجيه تبعية الكود المصدري في الاتجاه المعاكس لتدفق التحكم. هذا هو سبب تسميته بـ &amp;ldquo;&lt;strong>انعكاس&lt;/strong> التبعية&amp;rdquo;.&lt;/p>
&lt;h2 id="6-مثال-تنفيذي-محدد-باستخدام-typescript">6. مثال تنفيذي محدد باستخدام TypeScript
&lt;/h2>&lt;p>هنا، نوضح مثالًا بسيطًا لتنفيذ البنية النظيفة (ميزة تسجيل المستخدم) باستخدام TypeScript.&lt;/p>
&lt;h3 id="61-الكيانات-entities">6.1. الكيانات (Entities)
&lt;/h3>&lt;p>قواعد العمل الأكثر مركزية.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/domain/entities/User.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">User&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">id&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">createdAt&lt;/span>: &lt;span class="kt">Date&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// قواعد العمل الخاصة بالكيان (مثل التحقق من طول الاسم)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">public&lt;/span> &lt;span class="nx">isValid&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="kr">boolean&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span> &lt;span class="o">&amp;gt;=&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">includes&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;@&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-حالات-الاستخدام-use-cases">6.2. حالات الاستخدام (Use Cases)
&lt;/h3>&lt;p>في طبقة حالات الاستخدام، نحدد هياكل بيانات الإدخال والإخراج (DTO)، وواجهة المستودع لعكس التبعية.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/application/repositories/UserRepository.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">User&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../domain/entities/User&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// الواجهة المحددة بواسطة طبقة حالات الاستخدام
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">UserRepository&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">findByEmail&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">User&lt;/span> &lt;span class="err">|&lt;/span> &lt;span class="na">null&lt;/span>&lt;span class="p">&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>: &lt;span class="kt">User&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">void&lt;/span>&lt;span class="p">&amp;gt;;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/application/usecases/RegisterUser/RegisterUserDTO.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">export&lt;/span> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">RegisterUserInputDTO&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">interface&lt;/span> &lt;span class="nx">RegisterUserOutputDTO&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">id&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">createdAt&lt;/span>: &lt;span class="kt">Date&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/application/usecases/RegisterUser/RegisterUserUseCase.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">User&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../domain/entities/User&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">UserRepository&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../repositories/UserRepository&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">RegisterUserInputDTO&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">RegisterUserOutputDTO&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;./RegisterUserDTO&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">RegisterUserUseCase&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// يعتمد على التجريد (الواجهة). لا يعتمد على التنفيذ الملموس.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kr">private&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">userRepository&lt;/span>: &lt;span class="kt">UserRepository&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="nx">execute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">input&lt;/span>: &lt;span class="kt">RegisterUserInputDTO&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">RegisterUserOutputDTO&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">existingUser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">userRepository&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">findByEmail&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">input&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">existingUser&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;User already exists&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">newUser&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">User&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">crypto&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">randomUUID&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">input&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">input&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">new&lt;/span> &lt;span class="nb">Date&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">newUser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">isValid&lt;/span>&lt;span class="p">())&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Invalid user data&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// يستدعي عملية الحفظ في قاعدة البيانات الخارجية، لكن التبعية تتجه للداخل (للواجهة)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">userRepository&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">newUser&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">id&lt;/span>: &lt;span class="kt">newUser.id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">newUser.name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">newUser.email&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">createdAt&lt;/span>: &lt;span class="kt">newUser.createdAt&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="63-محولات-الواجهات-interface-adapters">6.3. محولات الواجهات (Interface Adapters)
&lt;/h3>&lt;p>نقوم بإنشاء عملية الوصول المحددة لقاعدة البيانات (تنفيذ المستودع) ووحدة التحكم التي تعالج طلبات HTTP.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/adapters/repositories/PostgresUserRepository.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">UserRepository&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../application/repositories/UserRepository&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">User&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../domain/entities/User&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// نفترض عميل قاعدة البيانات كطبقة خارجية (Driver)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">DatabaseClient&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../infrastructure/database/DatabaseClient&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">PostgresUserRepository&lt;/span> &lt;span class="kr">implements&lt;/span> &lt;span class="nx">UserRepository&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kr">private&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">dbClient&lt;/span>: &lt;span class="kt">DatabaseClient&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="nx">findByEmail&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">email&lt;/span>: &lt;span class="kt">string&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">User&lt;/span> &lt;span class="err">|&lt;/span> &lt;span class="na">null&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">record&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dbClient&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">query&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;SELECT * FROM users WHERE email = $1&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">]);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nx">record&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span> &lt;span class="kc">null&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">User&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">record&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">record&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">record&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">record&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">created_at&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="nx">save&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>: &lt;span class="kt">User&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">void&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">dbClient&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">query&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;INSERT INTO users (id, name, email, created_at) VALUES ($1, $2, $3, $4)&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">[&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">user&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">createdAt&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/adapters/controllers/UserController.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">RegisterUserUseCase&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../application/usecases/RegisterUser/RegisterUserUseCase&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">class&lt;/span> &lt;span class="nx">UserController&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">constructor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kr">private&lt;/span> &lt;span class="kr">readonly&lt;/span> &lt;span class="nx">registerUserUseCase&lt;/span>: &lt;span class="kt">RegisterUserUseCase&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">public&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="nx">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">Promise&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">void&lt;/span>&lt;span class="p">&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">input&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>: &lt;span class="kt">req.body.name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">email&lt;/span>: &lt;span class="kt">req.body.email&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">registerUserUseCase&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">execute&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">input&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">201&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">output&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">error&lt;/span>: &lt;span class="kt">any&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">res&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">status&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">400&lt;/span>&lt;span class="p">).&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">message&lt;/span>: &lt;span class="kt">error.message&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="64-المكون-الرئيسي-حقن-التبعية-di">6.4. المكون الرئيسي (حقن التبعية: DI)
&lt;/h3>&lt;p>عند بدء تشغيل التطبيق، نقوم ببناء جميع التبعيات (الربط). يُطلق على هذا اسم Composition Root.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// src/infrastructure/web/server.ts
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">import&lt;/span> &lt;span class="nx">express&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;express&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">DatabaseClient&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../database/DatabaseClient&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">PostgresUserRepository&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../adapters/repositories/PostgresUserRepository&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">RegisterUserUseCase&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../application/usecases/RegisterUser/RegisterUserUseCase&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">UserController&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s1">&amp;#39;../../adapters/controllers/UserController&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">app&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">express&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">use&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">express&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">json&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 1. تهيئة المحرك
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">dbClient&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">DatabaseClient&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="cm">/* معلومات الاتصال */&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 2. تهيئة المحول (إنشاء كائن من الفئة الملموسة)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">userRepository&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">PostgresUserRepository&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">dbClient&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 3. تهيئة حالة الاستخدام (حقن الفئة الملموسة في الواجهة = DI)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">registerUserUseCase&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">RegisterUserUseCase&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">userRepository&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 4. تهيئة وحدة التحكم
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">userController&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">UserController&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">registerUserUseCase&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// التوجيه
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">post&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/users&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="nx">userController&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">register&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">req&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">res&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">app&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">listen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">3000&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Server is running on port 3000&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>بهذه الطريقة، يتولى &amp;ldquo;نص التشغيل&amp;rdquo; الأبعد التعامل مع التفاصيل القذرة (إنشاء كائنات من الفئات الملموسة)، ويمرر واجهات نظيفة فقط للطبقات الداخلية، مما يؤدي إلى بنية تعزل منطق العمل تمامًا عن العالم الخارجي.&lt;/p>
&lt;h2 id="7-اعتبارات-رياضية-حول-الاقتران-والتماسك">7. اعتبارات رياضية حول الاقتران والتماسك
&lt;/h2>&lt;p>في هندسة البرمجيات، هناك مقاييس لتقييم جودة البنية وهي &lt;strong>الاقتران (Coupling)&lt;/strong> و &lt;strong>التماسك (Cohesion)&lt;/strong>.&lt;/p>
&lt;p>يمثل الاقتران $C$ قوة التبعية بين الوحدات (Modules). إذا كانت الوحدة $A$ تعتمد على الوحدة $B$، وافترضنا أن إجمالي عدد التبعيات في النظام هو $N_{dep}$، وعدد الوحدات هو $N_{mod}$، فإن أحد المقاييس التي تشير إلى التعقيد يمكن التعبير عنه على النحو التالي:&lt;/p>
$$ Complexity \propto \frac{N_{dep}}{N_{mod}} $$&lt;p>في البنية النظيفة، من خلال تطبيق DIP، نوجه سهم التبعية المادية نحو التجريد. تم تصميم وتيرة تغيير التجريد (الواجهة) (عدم الاستقرار: Instability $I$) لتكون منخفضة للغاية.&lt;/p>
&lt;p>يُحسب عدم الاستقرار $I$ بالصيغة التالية (كما عرفه Robert C. Martin):&lt;/p>
&lt;ul>
&lt;li>$C_e$ (Efferent Coupling): الاقتران الخارجي (عدد التبعيات الصادرة)&lt;/li>
&lt;li>$C_a$ (Afferent Coupling): الاقتران الداخلي (عدد التبعيات الواردة)&lt;/li>
&lt;/ul>
$$ I = \frac{C_e}{C_e + C_a} $$&lt;ul>
&lt;li>عندما يكون $I = 0$، يكون المكون مستقرًا تمامًا (لا يعتمد على أحد، ويعتمد عليه الآخرون).&lt;/li>
&lt;li>عندما يكون $I = 1$، يكون المكون غير مستقر تمامًا (لا يعتمد عليه أحد، ويعتمد هو على الآخرين).&lt;/li>
&lt;/ul>
&lt;p>نظرًا لأن &amp;ldquo;طبقة الكيانات&amp;rdquo; في البنية النظيفة لها $C_e = 0$ (لا تعتمد على الخارج)، فإنها تصبح $I = 0$. بعبارة أخرى، إنها الطبقة الأكثر استقرارًا.
على العكس من ذلك، &amp;ldquo;طبقة واجهة المستخدم&amp;rdquo; أو &amp;ldquo;طبقة قاعدة البيانات&amp;rdquo; يكون لها $C_a \approx 0$ و $C_e > 0$، لذلك تصبح $I \approx 1$، مما يجعلها طبقات سهلة التغيير (طبقات غير مستقرة).&lt;/p>
&lt;p>ينص المبدأ المهم للبنية SDP (Stable Dependencies Principle: مبدأ التبعيات المستقرة) على أن &amp;ldquo;&lt;strong>التبعية يجب أن تتجه نحو مكون أكثر استقرارًا (مكون ذو قيمة $I$ أصغر)&lt;/strong>&amp;rdquo;. الدوائر متحدة المركز للبنية النظيفة هي تصور دقيق لـ SDP، وهي مصممة بحيث تتجه التبعيات من الخارج ($I=1$) إلى الداخل ($I=0$).&lt;/p>
&lt;h2 id="8-استراتيجية-الاختبار-والبنية-النظيفة">8. استراتيجية الاختبار والبنية النظيفة
&lt;/h2>&lt;p>إحدى أكبر مزايا البنية النظيفة هي &lt;strong>سهولة الاختبار&lt;/strong>. نظرًا لفصل الطبقات، يمكنك كتابة اختبارات لكل طبقة بشكل مستقل.&lt;/p>
&lt;h3 id="81-اختبار-الكيانات-unit-test">8.1. اختبار الكيانات (Unit Test)
&lt;/h3>&lt;p>نظرًا لأنه منطق نقي خالٍ من أي تبعيات خارجية، فليس هناك حاجة لقاعدة بيانات أو كائنات وهمية (mocks). إنه أسرع اختبار موثوق يمكنك تشغيله.&lt;/p>
&lt;h3 id="82-اختبار-حالات-الاستخدام-unit-test-with-mocks">8.2. اختبار حالات الاستخدام (Unit Test with Mocks)
&lt;/h3>&lt;p>نظرًا لأن التبعيات الخارجية مثل المستودعات يتم تعريفها جميعًا كواجهات، فإن كل ما عليك فعله أثناء الاختبار هو حقن (DI) &lt;strong>كائنات وهمية أو تنفيذ في الذاكرة (Fake)&lt;/strong> مخصص للاختبار. لا داعي لتشغيل قاعدة بيانات فعلية. يتيح لك ذلك اختبار التفرعات المعقدة لمنطق العمل ومعالجة الاستثناءات بسرعات عالية.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// مثال لاختبار حالة الاستخدام (بافتراض Jest)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">test&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;محاولة التسجيل ببريد إلكتروني موجود مسبقًا يجب أن ينتج عنها خطأ&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// إنشاء مستودع مزيف (Fake)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">mockRepo&lt;/span>: &lt;span class="kt">UserRepository&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">findByEmail&lt;/span>: &lt;span class="kt">async&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">email&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">User&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;1&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;Test&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">email&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nb">Date&lt;/span>&lt;span class="p">()),&lt;/span> &lt;span class="c1">// إرجاع مستخدم موجود
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">save&lt;/span>: &lt;span class="kt">async&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">user&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">useCase&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">RegisterUserUseCase&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">mockRepo&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// تنفيذ حالة الاستخدام والتأكد من الخطأ
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">expect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">useCase&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">execute&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;Bob&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">email&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;test@example.com&amp;#39;&lt;/span> &lt;span class="p">}))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">rejects&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">.&lt;/span>&lt;span class="nx">toThrow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;User already exists&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="83-اختبار-المحولات-integration-test">8.3. اختبار المحولات (Integration Test)
&lt;/h3>&lt;p>تختبر فئات تنفيذ المستودع فعليًا الاتصال بقاعدة البيانات وما إذا كان SQL صحيحًا. يختبر اختبار وحدة التحكم تلقي طلب HTTP وإرجاع JSON. هنا لا نقوم بالتحقق المفصل من منطق العمل، بل نكتفي بتأكيد صحة &amp;ldquo;التحويل&amp;rdquo; و&amp;quot;الاتصال&amp;quot;.&lt;/p>
&lt;h2 id="9-عيوب-البنية-النظيفة-ومتى-يجب-اعتمادها">9. عيوب البنية النظيفة ومتى يجب اعتمادها
&lt;/h2>&lt;p>البنية النظيفة التي تبدو قادرة على كل شيء ليست رصاصة فضية. هناك عيوب (مفاضلات) مثل ما يلي:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>زيادة تكلفة التعلم الأولية وتكلفة التطوير&lt;/strong>: يزداد عدد الملفات والواجهات (التجريدات) بشكل كبير. سيكون هناك الكثير من &amp;ldquo;الأكواد النمطية (boilerplate code)&amp;rdquo; مثل إعادة حزم DTOs.&lt;/li>
&lt;li>&lt;strong>مبالغة في المشاريع الصغيرة&lt;/strong>: في النماذج الأولية التي يتم إنشاؤها في أيام قليلة أو الأدوات التي تُستخدم لمرة واحدة والتي نادرًا ما تتغير، غالبًا ما تكون هذه البنية تكلفة مهدرة. إنها أيضًا غير مناسبة لواجهات برمجة التطبيقات البسيطة التي تقوم فقط بعمليات CRUD.&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>متى يجب اعتمادها&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>المنتجات التي يُتوقع الحفاظ عليها وتشغيلها لفترة طويلة (عدة سنوات أو أكثر).&lt;/li>
&lt;li>الأنظمة ذات قواعد العمل المعقدة حيث تتغير المتطلبات بشكل متكرر.&lt;/li>
&lt;li>عندما ترغب في تقسيم العمل في فرق تطوير كبيرة (الواجهة الأمامية، الخلفية، البنية التحتية، إلخ).&lt;/li>
&lt;li>عندما تريد تصميم مجال عمل معقد بالتزامن مع التصميم المدفوع بالمجال (DDD: Domain-Driven Design).&lt;/li>
&lt;/ul>
&lt;h2 id="10-الخلاصة">10. الخلاصة
&lt;/h2>&lt;p>البنية النظيفة هي فلسفة تصميم لحماية جوهر النظام المسمى &amp;ldquo;قواعد العمل&amp;rdquo; من &amp;ldquo;التفاصيل&amp;rdquo; مثل واجهة المستخدم وقاعدة البيانات وأطر العمل.&lt;/p>
&lt;p>يكمن جوهر هذا في &lt;strong>قاعدة التبعية&lt;/strong> و &lt;strong>مبدأ انعكاس التبعية (DIP)&lt;/strong>. من خلال تطبيقها بشكل صحيح، تصبح البرمجيات مرنة للتغيير، وأسهل للاختبار، ويمكنها الحفاظ على قيمتها على مدى فترات طويلة.&lt;/p>
&lt;p>الأمر المهم ليس التقليد الأعمى لهيكل أدلة البنية النظيفة، بل فهم جوهر أسئلة مثل &amp;ldquo;&lt;strong>لماذا نقسمها بهذه الطريقة&lt;/strong>&amp;rdquo; و&amp;quot;&lt;strong>أين تشير أسهم التبعية&lt;/strong>&amp;quot;، وتطبيقها بشكل مناسب وفقًا لحجم مشروعك ومدى تعقيده.&lt;/p>
&lt;hr>
&lt;p>&lt;em>مرجع: &amp;ldquo;Clean Architecture: A Craftsman&amp;rsquo;s Guide to Software Structure and Design&amp;rdquo; بقلم Robert C. Martin&lt;/em>&lt;/p></description></item></channel></rss>