
الخلفية الحديثة متنوعة ، لكنها ما زالت تطيع بعض القواعد غير المعلنة. يواجه العديد منا الذين يقومون بتطوير تطبيقات الخادم طرقًا مقبولة عمومًا ، مثل Clean Architecture و SOLID و Ignorance Prognence و Dependency Injection و غيرها. يتم اختراق العديد من سمات تطوير الخادم بحيث لا تثير أي أسئلة ويتم استخدامها بدون تفكير. يتحدثون كثيرًا عن البعض ، لكن لا يستخدمونه أبدًا. يتم تفسير معنى الباقي بشكل غير صحيح أو مشوه. يتحدث المقال عن كيفية بناء بنية خلفية بسيطة ونموذجية تمامًا ، والتي لا يمكنها فقط اتباع مبادئ منظري البرمجة المشهورين دون أي ضرر ، بل يمكنها أيضًا تحسينها إلى حد ما.
مخصص لكل من لا يفكر في البرمجة دون جمال ولا يقبل الجمال في خضم العبثية.نموذج المجال
النمذجة هي المكان الذي يجب أن يبدأ فيه تطوير البرمجيات في عالم مثالي. لكننا جميعًا غير مثاليين ، نتحدث كثيرًا عن ذلك ، لكننا نفعل كل شيء كالمعتاد. في كثير من الأحيان السبب هو النقص في الأدوات الموجودة. ولكي نكون صادقين ، لدينا الكسل والخوف من تحمل المسؤولية للابتعاد عن "أفضل الممارسات". في عالم غير كامل ، يبدأ تطوير البرامج ، في أحسن الأحوال ، بالسقالات ، وفي أسوأ الأحوال ، لا يتم عمل أي شيء مع تحسين الأداء. ومع ذلك ، أود أن أتجاهل الأمثلة الصعبة للمهندسين المعماريين "المتميزين" والتكهن بأمور عادية.
لذلك ، لدينا مهمة فنية ، وحتى لدينا تصميم واجهة مستخدم (أو لا ، إذا كانت واجهة المستخدم غير متوفرة). والخطوة التالية هي لتعكس المتطلبات في نموذج المجال. للبدء ، يمكنك رسم رسم تخطيطي لكائنات النماذج للتوضيح:

بعد ذلك ، كقاعدة عامة ، نبدأ في عرض النموذج على وسائل تنفيذه - لغة برمجة ، أو مخطط كائن مرتبط بالعلاقة (ORM) ، أو على نوع ما من إطار عمل معقد مثل ASP.NET MVC أو Ruby on Rails ، بمعنى آخر - البدء في كتابة التعليمات البرمجية. في هذه الحالة ، نحن نتبع مسار الإطار ، الذي أعتقد أنه غير صحيح في إطار التطوير المستند إلى النموذج ، بغض النظر عن مدى ملائمته في البداية. هنا تقوم بافتراض كبير ، والذي ينفي لاحقًا فوائد التطوير المستند إلى المجال. كخيار أكثر حرية ، لا يقتصر على نطاق أي أداة ، أود أن أقترح الخوض في استخدام الأدوات النحوية فقط من لغة البرمجة لبناء نموذج كائن لمنطقة الموضوع. في عملي ، أستخدم عدة لغات برمجة - C # ، JavaScript ، Ruby. أصدر Fate مرسومًا بأن نظامي Java و C # هما مصدر إلهامي ، وأن JS هو الدخل الرئيسي لي ، وروبي هي اللغة التي أحبها. لذلك ، سأستمر في عرض أمثلة بسيطة في روبي: أنا مقتنع بأن هذا لن يسبب مشاكل للمطورين باللغات الأخرى لفهمها. لذا ، قم بتوصيل النموذج إلى فئة الفاتورة في روبي:
class Invoice attr_reader :amount, :date, :created_at, :paid_at def initialize(attrs, payment_service) @created_at = DateTime.now @paid_at = nil @amount = attrs[:amount] @date = attrs[:date] @subscription = attrs[:subscription] @payment_service = payment_service end def pay credit_card = @subscription.customer.credit_card amount = @subscription.plan.price @payment_service.charge(credit_card, amount) @paid_at = DateTime.now end end
أي لدينا فئة يقبل مُنشئها مجموعة من السمات ، تبعيات الكائن ، ويقوم بتهيئة حقوله ، وطريقة الدفع التي يمكن أن تغير حالة الكائن. كل شيء بسيط جدا. الآن لا نفكر في كيفية ومكان عرض وتخزين هذا الكائن. إنه موجود فقط ، يمكننا إنشائه وتغيير حالته والتفاعل مع الكائنات الأخرى. يرجى ملاحظة أن الكود لا يحتوي على أي قطع أثرية أجنبية مثل BaseEntity وغيرها من القمامة غير المرتبطة بالنموذج. هذا مهم جدا بالمناسبة ، في هذه المرحلة يمكننا بالفعل البدء في التطوير من خلال الاختبار (TDD) ، وذلك باستخدام كائنات كعب الروتين بدلاً من التبعيات مثل payment_service:
RSpec.describe Invoice do before :each do @payment_service = double(:payment_service) allow(@payment_service).to receive(:charge) @amount = 100 @credit_card = CreditCard.new({...}) @customer = Customer.new({credit_card: @credit_card, ...}) @subscription = Subscription.new({customer: customer, ...}) @invoice = Invoice.new({amount: @amount, date: DateTime.now, @subscription: subscription}, payment_service) end describe 'pay' do it "charges customer's credit card" do expect(@payment_service).to receive(:charge).with(@credit_card, @amount) @invoice.pay end it 'makes the invoice paid' do expect(@invoice.paid_at).not_to be_nil @invoice.pay end end end
أو حتى اللعب مع النموذج في المترجم (IRB لـ Ruby) ، والذي قد يكون ، على الرغم من أنه غير ودود للغاية ، واجهة المستخدم:
irb > invoice = Invoice.new({amount: @amount, date: DateTime.now, @subscription: subscription}, payment_service) irb > invoice.pay
لماذا من المهم للغاية تجنب "القطع الأثرية الأجنبية" في هذه المرحلة؟ والحقيقة هي أن النموذج لا ينبغي أن يكون لديه أي فكرة عن كيفية حفظه أو ما إذا كان سيتم حفظه على الإطلاق. في النهاية ، قد يكون تخزين الكائنات في الذاكرة في بعض الأنظمة مناسبًا تمامًا. في وقت النمذجة ، يجب أن نستخلص تماما من هذه التفاصيل. ويسمى هذا النهج الجهل المستمر. يجب التأكيد على أننا لا نتجاهل مشكلات العمل مع المستودع ، سواء أكانت قاعدة بيانات علائقية أو أي قاعدة بيانات أخرى ، فإننا نتجاهل فقط تفاصيل التفاعل معها في مرحلة النمذجة. يعني الجهل المستمر الإزالة المتعمدة لآليات العمل مع حالة النموذج ، وكذلك جميع أنواع البيانات الوصفية المرتبطة بهذه العملية ، من النموذج نفسه. الأمثلة على ذلك:
يرجع هذا النهج أيضًا إلى أسباب أساسية - الامتثال لمبدأ المسؤولية الفردية (مبدأ المسؤولية الفردية ، S في SOLID). إذا كان النموذج ، بالإضافة إلى مكونه الوظيفي ، يصف معلمات المحافظة على الحالة ويتعامل أيضًا مع الحفظ والتحميل ، فمن الواضح أنه يتحمل العديد من المسؤوليات. الميزة الناتجة وليست الميزة الأخيرة لجهل المثابرة هي القدرة على استبدال أداة التخزين وحتى نوع التخزين نفسه أثناء عملية التطوير.
نموذج عرض المراقب المالي
مفهوم MVC شائع جدًا في بيئة التطوير الخاصة بالتطبيقات المختلفة ، وليس فقط الخوادم ، بلغات ومنصات مختلفة ، بحيث لم نعد نفكر في ماهية هذا السبب ولماذا كانت هناك حاجة إليه على الإطلاق. لدي أكثر الأسئلة من هذا الاختصار يسمى "المراقب المالي". من وجهة نظر تنظيم بنية الكود ، من الجيد تجميع الإجراءات على النموذج. لكن يجب ألا تكون وحدة التحكم فئة على الإطلاق ، بل يجب أن تكون وحدة نمطية تتضمن طرقًا للوصول إلى النموذج. ليس ذلك فحسب ، هل يجب أن يكون له مكان على الإطلاق؟ كمطور تابع مسار .NET -> Ruby -> Node.js ، لقد تأثرت ببساطة بوحدات التحكم JS (ES5) التي تنفذ في إطار Express.js. من خلال القدرة على حل المهمة الموكلة إلى وحدات التحكم بأسلوب أكثر وظيفية ، يقوم المطورون ، كما سحروا ، بكتابة السحر "Controller" مرارًا وتكرارًا. لماذا وحدة تحكم نموذجية سيئة؟
وحدة التحكم النموذجية هي مجموعة من الأساليب التي لا ترتبط ارتباطًا وثيقًا ببعضها البعض ، متحدة من خلال طريقة واحدة فقط - وهي جوهر معين من النموذج ؛ وأحيانًا ليس واحدًا ، أسوأ. قد تتطلب كل طريقة فردية تبعيات مختلفة. بالنظر إلى الأمام قليلاً ، ألاحظ أنني مؤيد لممارسة انعكاس التبعية (انعكاس التبعية ، D في SOLID). لذلك ، أحتاج إلى تهيئة هذه التبعيات في مكان ما بالخارج وتمريرها إلى مُنشئ وحدة التحكم. على سبيل المثال ، عند إنشاء حساب جديد ، يجب أن أرسل إعلامات إلى المحاسب ، والتي أحتاج إلى خدمة إشعار لها ، وفي طرق أخرى لا أحتاج إليها:
class InvoiceController def initialize(invoice_repository, notification_service) @repository = invoice_repository @notification_service = notification_service end def index @repository.get_all end def show(id) @repository.get_by_id(id) end def create(data) @repository.create(data) @notification_service.notify_accountant end end
تكمن الفكرة هنا في تقسيمها إلى طرق للعمل مع النموذج إلى فصول منفصلة ، ولماذا لا؟
class ListInvoices def initialize(invoice_repository) @repository = invoice_repository end def call @repository.get_all end end class CreateInvoice def initialize(invoice_repository, notification_service) @repository = invoice_repository @notification_service = notification_service end def call @repository.create(data) @notification_service.notify_accountant end end
حسنًا ، بدلاً من وحدة التحكم ، يوجد الآن مجموعة من "الوظائف" للوصول إلى النموذج ، والتي ، بالمناسبة ، يمكن أيضًا تنظيمها باستخدام دلائل نظام الملفات ، على سبيل المثال. أنت الآن بحاجة إلى "فتح" هذه الأساليب للخارج ، أي تنظيم شيء مثل جهاز التوجيه. باعتباري شخصًا يغري جميع أنواع DSL (لغة خاصة بالمجال) ، فإنني أفضل الحصول على وصف مرئي أكثر لتعليمات تطبيق ويب من الحيل في روبي أو لغة أخرى للأغراض العامة لتحديد المسارات:
`HTTP GET /invoices -> return all invoices` `HTTP POST /invoices -> create new invoice`
أو على الأقل
`HTTP GET /invoices -> ./invoices/list_invoices` `HTTP POST /invoices -> ./invoices/create`
هذا يشبه إلى حد بعيد جهاز التوجيه النموذجي ، مع وجود اختلاف وحيد يتمثل في أنه لا يتفاعل مع وحدات التحكم ، ولكن بشكل مباشر مع الإجراءات في النموذج. من الواضح أننا إذا أردنا إرسال واستقبال JSON ، فعلينا أن نتحلى بالتسلسل وإلغاء تسلسل الأشياء وأكثر من ذلك بكثير. بطريقة أو بأخرى ، يمكننا التخلص من وحدات التحكم ، ونقل جزء من مسؤوليتها إلى بنية الدليل وجهاز التوجيه الأكثر تقدما.
حقن التبعية
كتبت عمدا "راوتر أكثر تقدما". من أجل أن يكون جهاز التوجيه قادرًا حقًا على السماح بتدفق الإجراءات على النموذج باستخدام آلية حقن التبعية على المستوى الإعلاني ، فمن المحتمل أن يكون معقدًا تمامًا من الداخل. يجب أن يبدو المخطط العام لعمله مثل هذا:

كما ترون ، فإن جهاز التوجيه الخاص بي بالكامل مليء بحقن التبعية باستخدام حاوية IoC. لماذا هذا ضروري حتى؟ يعود مفهوم "حقن التبعية" إلى تقنية انعكاس التبعية ، والتي تم تصميمها لتقليل توصيل الكائنات عن طريق تحريك تهيئة التبعية خارج نطاق استخدامها. مثال:
class Repository; end
يساعد هذا النهج بشكل كبير أولئك الذين يستخدمون تطوير اختبار يحركها. في المثال أعلاه ، يمكننا بسهولة وضع روتين في المُنشئ بدلاً من كائن المستودع الحقيقي المطابق لواجهته ، دون "اختراق" نموذج الكائن. ليست هذه هي مكافأة DI الوحيدة: عند تطبيقها بشكل صحيح ، ستجلب هذه الطريقة الكثير من السحر اللطيف إلى التطبيق الخاص بك ، ولكن أول الأشياء أولاً. Dependency Injection هو نهج يسمح لك بدمج تقنية Invendency Inversion في حل معماري كامل. عادة ما تكون أداة التنفيذ حاوية IoC- (انقلاب التحكم). هناك الكثير من حاويات IoC الرائعة حقًا في عالم Java و .NET ، وهناك العشرات منها. في JS و Ruby ، للأسف ، لا توجد خيارات مناسبة لي. على وجه الخصوص ، نظرت إلى الحاوية
الجافة (الحاوية
الجافة ). سيكون هذا ما سيبدو عليه صفي عند استخدامه:
class Invoice include Import['payment_service'] def pay credit_card = @subscription.customer.credit_card amount = @subscription.plan.price @payment_service.charge(credit_card, amount) end end
بدلاً من الاستخدام الرفيع للمنشئ ، نثقل كاهل الفصل من خلال تقديم تبعياتنا الخاصة ، والتي في المرحلة الأولية تقودنا بعيدًا عن نموذج نظيف ومستقل. حسنًا ، يجب ألا يعرف النموذج عن IoC على الإطلاق! هذا صحيح بالنسبة لإجراءات مثل CreateInvoice. بالنسبة للحالة المعينة ، في اختباراتي ، أنا ملزم بالفعل باستخدام IoC كشيء غير قابل للتصرف. هذا خطأ تماما. يجب ألا تعرف كائنات التطبيق بالنسبة للجزء الأكبر عن وجود IoC. بعد البحث والتفكير كثيرًا ،
رسمت رسالتي IoC ، والتي لن تكون تدخلية للغاية.
حفظ وتحميل نموذج
الجهل المستمر يتطلب محول كائن غير مزعجة. في هذه المقالة ، أعني العمل مع قاعدة بيانات علائقية ، النقاط الرئيسية ستكون صحيحة بالنسبة لأنواع أخرى من المخازن. يتم استخدام محول الكائن العلائقية - ORM (كائن Relational معين) كمحول مماثل لقواعد البيانات العلائقية. في عالم .NET و Java ، هناك وفرة من أدوات ORM القوية حقًا. كل منهم لديه بعض أو غيرها من العيوب الطفيفة التي يمكنك تغمض عينيك. لا توجد حلول جيدة في JS وروبي. كلهم ، بطريقة أو بأخرى ، يربطون النموذج بشكل صارم بالإطار ويفرضون إعلان العناصر الأجنبية ، ناهيك عن عدم قابلية الجهل بالثبات. كما هو الحال في IoC ، فكرت في تنفيذ ORM بنفسي ، هذا هو الوضع في روبي. لم أفعل كل شيء من البداية ، لكنني اتخذت أساسًا ORM Sequel بسيطًا ، والذي يوفر أدوات غير مزعجة للعمل مع أنظمة إدارة قواعد البيانات العلائقية المختلفة. بادئ ذي بدء ، كنت مهتمًا بالقدرة على تنفيذ الاستعلامات في شكل SQL منتظم ، وتلقي مجموعة من السلاسل (كائنات التجزئة) في الإخراج. بقي فقط لتنفيذ معينك وتوفير الجهل الثبات. كما ذكرت من قبل ، لا أريد مزج حقول التعيين في نموذج المجال ، لذلك أقوم بتطبيق مخطط معين بحيث يستخدم ملف تكوين منفصل بتنسيق الكتابة:
entity Invoice do field :amount field :date field :start_date field :end_date field :created_at field :updated_at reference :user, type: User reference :subscription, type: Subscription end
إهمال الجهل سهل التنفيذ باستخدام كائن خارجي من نوع السجل:
repository.save(user)
لكن سنذهب أبعد من ذلك وننفذ نمط وحدة العمل. للقيام بذلك ، تحتاج إلى تسليط الضوء على مفهوم الدورة. الجلسة عبارة عن كائن موجود بمرور الوقت ، يتم خلاله تنفيذ مجموعة من الإجراءات على النموذج ، وهي عملية منطقية واحدة. على مدار الجلسة ، يمكن أن يحدث تحميل وتغيير كائنات النموذج. في نهاية الجلسة ، يحدث الحفاظ على المعاملات لحالة النموذج.
مثال وحدة العمل:
user = session.load(User, id: 1) plan = session.load(Plan, id: 1) subscription = Subscription.new(user, plan) session.attach(subscription) invoice = Invoice.new(subscription) session.attach(invoice)
نتيجة لذلك ، سيتم تنفيذ إرشادات 2 في قاعدة البيانات بدلاً من 4 ، وسيتم تنفيذ الاثنين في نفس المعاملة.
ثم فجأة تذكر المستودعات! يوجد هنا شعور بـ deja vu ، كما هو الحال مع وحدات التحكم: أليس المستودع هو نفس الكيان البدائي؟ بالتطلع إلى الأمام ، سأجيب - نعم ، إنها كذلك. الغرض الرئيسي من المستودع هو حفظ طبقة منطق العمل من التفاعل مع التخزين الحقيقي. على سبيل المثال ، في سياق قواعد البيانات العلائقية ، فهذا يعني كتابة استعلامات SQL مباشرةً في رمز منطق العمل. مما لا شك فيه ، هذا قرار معقول للغاية. لكن العودة للحظة عندما تخلصنا من وحدة التحكم. من وجهة نظر OOP ، يكون المستودع أساسًا هو نفس وحدة التحكم - نفس مجموعة الطرق ، ليس فقط لمعالجة الطلبات ، ولكن للعمل مع المستودع. يمكن أيضًا تقسيم المستودع إلى أعمال. بكل المؤشرات ، لن تختلف هذه الإجراءات بأي شكل من الأشكال عن ما اقترحناه بدلاً من وحدة التحكم. وهذا هو ، يمكننا رفض مستودع والتحكم لصالح عمل موحد واحد!
class LoadPlan def initialize(session) @session = session end def call sql = <<~SQL SELECT p.* AS ENTITY plan FROM plans p WHERE p.id = 1 SQL @session.fetch(Plan, sql) end end
ربما لاحظت أنني أستخدم SQL بدلاً من نوع من بناء جملة الكائن. هذه مسألة ذوق. أفضّل SQL لأنها لغة استعلام ، وهي نوع من DSL للتعامل مع البيانات. من الواضح أنه من الأسهل دائمًا كتابة Plan.load (id) مقارنة بـ SQL المقابل ، ولكن هذا ينطبق على الحالات البسيطة. عندما يتعلق الأمر بأشياء أكثر تعقيدًا قليلاً ، تصبح SQL أداة موضع ترحيب كبير. أحيانًا تلعن ORM آخر في محاولة لجعلها تفعل مثل SQL الخالص ، والتي "سأكتب في بضع دقائق." بالنسبة لأولئك الذين هم في شك ، أقترح النظر في
وثائق MongoDB ، حيث يتم تقديم التفسيرات في شكل يشبه SQL ، والتي تبدو مضحكة للغاية! لذلك ، فإن واجهة الاستعلامات في
ORM JetSet ، التي كتبت لأغراضي ، هي لغة SQL مع الحد الأدنى من التشريب مثل "AS ENTITY". بالمناسبة ، في معظم الحالات ، لا أستخدم الكائنات النموذجية ، ومختلف DTOs ، إلخ. لعرض بيانات جدولية - أنا فقط أكتب استعلام SQL ، وأحصل على مجموعة من كائنات التجزئة وأعرضها في طريقة العرض. بطريقة أو بأخرى ، لا يتمكن سوى عدد قليل من الأشخاص من "تمرير" البيانات الكبيرة عن طريق عرض الجداول ذات الصلة على نموذج. في الممارسة العملية ، يُرجح استخدام الإسقاط المسطح (العرض) ، وتأتي المنتجات الناضجة جدًا إلى مرحلة التحسين عندما يبدأ استخدام حلول أكثر تعقيدًا مثل CQRS (فصل مسؤولية القيادة والاستعلام).
وضع كل ذلك معا
إذن ما لدينا:
- لقد توصلنا إلى كيفية تحميل النموذج وحفظه ، كما صممنا بنية تقريبية لأداة توصيل الويب الخاصة بالطراز ، وجهاز توجيه معين ؛
- لقد توصلنا إلى استنتاج مفاده أن كل المنطق الذي لا يمثل جزءًا من مجال الموضوع يمكن أخذه إلى إجراءات (إجراءات) بدلاً من وحدات التحكم والمستودعات ؛
- يجب أن تدعم الإجراءات حقن التبعية
- أداة لائقة Dependency Injection تنفيذها ؛
- يتم تنفيذ ORM اللازمة.
الشيء الوحيد المتبقي هو تنفيذ نفس "الموجه". نظرًا لأننا تخلصنا من المستودعات ووحدات التحكم لصالح الإجراءات ، فمن الواضح أننا بحاجة إلى تنفيذ عدة إجراءات لطلب واحد. الإجراءات مستقلة ولا يمكننا الاستثمار في بعضنا البعض. لذلك ، كجزء من
إطار عمل Dandy ، قمت بتنفيذ جهاز توجيه يسمح لك بإنشاء سلاسل من الإجراءات. مثال التكوين (انتبه / خطط):
:receive .-> :before -> common/open_db_session GET -> welcome -> :respond <- show_welcome /auth -> :before -> current_user@users/load_current_user /profile -> GET -> plan@plans/load_plan \ -> :respond <- users/show_user_profile PATCH -> users/update_profile /plans -> GET -> current_plan@plans/load_current_plan \ -> plans@plans/load_plans \ -> :respond <- plans/list :catch -> common/handle_errors
يعرض "الحصول على / مصادقة / خطط" جميع خطط الاشتراك المتاحة و "إبراز" الخطة الحالية. يحدث ما يلي:
- ": قبل -> Common / open_db_session" - فتح جلسة JetSet
- / auth ": قبل -> current_user @ users / load_current_user" - قم بتحميل المستخدم الحالي (بواسطة الرموز المميزة). يتم تسجيل النتيجة في حاوية IoC كـ current_user (current_user @ instruction).
- / auth / plans "current_plan @ plans / load_current_plan" - قم بتحميل الخطة الحالية. لهذا ، يتم أخذ القيمةcurrent_user من الحاوية. يتم تسجيل النتيجة في حاوية IoC كـ current_plan (current_plan @ instruction):
class LoadCurrentPlan def initialize(current_user, session) @current_user = current_user @session = session end def call sql = <<~SQL SELECT p.* AS ENTITY plan FROM plans p INNER JOIN subscriptions s ON s.user_id = :user_id AND s.current = 't' WHERE p.id = :user_id LIMIT 1 SQL @session.execute(sql, user_id: @current_user.id) do |row| map(Plan, row, 'plan') end end end
- "Plans @ plans / load_plans" - تحميل قائمة بجميع الخطط المتاحة. يتم تسجيل النتيجة في حاوية IoC كخطط (the plans @ instruction).
- ": استجابة <- plans / list" - يقوم ViewBuilder المسجل ، على سبيل المثال JBuilder ، برسم "خطط / قائمة" View من النوع:
json.plans @plans do |plan| json.id plan.id json.name plan.name json.price plan.price json.active plan.id == @current_plan.id end
كماplans وcurrent_plan ، يتم استرداد القيم المسجلة في الخطوات السابقة من الحاوية. في مُنشئ الإجراء Action ، بشكل عام ، يمكنك "ترتيب" كل ما تحتاجه ، أو بالأحرى كل شيء مسجل في الحاوية. من المحتمل أن يكون للقارئ اليقظ سؤال ، لكن هل هناك عزلة لهذه المتغيرات في وضع "متعدد المستخدمين"؟ نعم هو كذلك. الحقيقة هي أن حاوية Hypo IoC لديها القدرة على تعيين عمر الكائنات ، علاوة على ذلك ، ربطها بعمر الكائنات الأخرى. داخل Dandy ، تكون المتغيرات مثلplans وcurrent_plan وcurrent_user مرتبطة بكائن الطلب وسيتم إتلافها فور اكتمال الطلب. بالمناسبة ، ترتبط جلسة عمل JetSet أيضًا بالطلب - سيتم أيضًا إعادة تعيين حالته عند اكتمال طلب Dandy. أي كل طلب له سياق معزول. يحكم هيبو دورة حياة داندي بأكملها ، بغض النظر عن مدى متعة هذا التورية في الترجمة الحرفية للأسماء.
النتائج
في إطار بنية معينة ، استخدم نموذج الكائن لوصف مجال الموضوع ؛ أستخدم الممارسات المناسبة مثل Dependency Injection ؛ يمكنني حتى استخدام الميراث. ولكن ، في الوقت نفسه ، كل هذه الإجراءات هي وظائف عادية بشكل أساسي يمكن ربطها معًا على مستوى إعلاني. لقد حصلنا على الخلفية المطلوبة بأسلوب وظيفي ، ولكن مع كل مزايا أسلوب الكائن ، عندما لا تواجه مشكلات مع التجريدات واختبار الكود. باستخدام جهاز توجيه DSL Dandy كمثال ، نحن أحرار في إنشاء اللغات اللازمة لوصف الطرق والمزيد.
استنتاج
كجزء من هذه المقالة ، قمت بإجراء رحلة استكشافية على الجوانب الأساسية لإنشاء خلفية كما أراها. أكرر ، المقال سطحي ، ولم يمس العديد من الموضوعات المهمة ، مثل تحسين الأداء على سبيل المثال. حاولت التركيز فقط على تلك الأشياء التي يمكن أن تكون مفيدة حقًا للمجتمع كغذاء للتفكير ، وليس مرة أخرى من فراغ إلى فارغ ، ما هو SOLID و TDD وكيف يبدو مخطط MVC وما إلى ذلك. يمكن العثور بسهولة على تعريفات صارمة لهذه المصطلحات وغيرها التي يستخدمها قارئ فضولي في الشبكة الواسعة ، ناهيك عن الزملاء في المتجر ، الذين تشكل هذه الاختصارات جزءًا من الكلام اليومي. وأخيرًا ، أؤكد ، حاول ألا تركز على الأدوات التي كنت بحاجة لتطبيقها لحل المشكلات المطروحة.
هذا مجرد دليل على صحة الأفكار ، وليس جوهرها. إذا كانت هذه المقالة ذات أهمية ، فسأكتب مادة منفصلة عن هذه المكتبات.