مرحبًا بالعالم!
لنبدأ بـ Action لا يفعل شيئًا مفيدًا — يطبع سطرًا ويُعيد stub فارغًا. قيمته في مكان آخر: فهو يُظهر الحد الأدنى من الإعلانات التي بدونها لن يُشغّل AOA أي شيء على الإطلاق.
$ uv run python examples/step_01_Action_and_pipeline/01_hello_world.pyهذا عدد لا بأس به من الأسطر لتحية بهذه البساطة — لكن لا واحد منها طقس شكلي: كل واحد يُعلن أمرًا لا يُعدّ الـ Action في AOA مكتملًا بدونه. يبدأ كل شيء بـdomain. @meta هو جواز سفر الـ Action؛ @check_roles(GuestRole) يُعلن الوصول صراحةً، لا افتراضيًا. أما @summary_aspect فهو نقطة الخروج الوحيدة: تجاوز أيًّا من الثلاثة، وسترفض الآلة تشغيل الـ Action حتى قبل أول استدعاء.
Params، Result، وbox
الـ stubs جيدة للمقدمات، أما الـ Action الحقيقي فيتعامل مع البيانات. لنُضِف له مدخلًا ومخرجًا — ولنستبدل print بالأداة التي يتواصل بها الـ Action في AOA مع العالم الخارجي.
$ uv run python examples/step_01_Action_and_pipeline/02_params_result_and_box.pybox أكثر إثارة للاهتمام من print — فهو logger مُهيكَل مرتبط بالخطوة الحالية: يحمل قناة، ومستوى، وdomain، واسم action، وaspect، لذا يسهل إخضاعه للتصفية والتوجيه والتصدير. أما إلى أين يذهب الحدث فذلك ليس قرار الـ Action نفسه: تلك مسؤولية الآلة، المُوصَّلة من الخارج.
عدة aspects
نادرًا ما تكفي خطوة واحدة. لا يترك AOA للبيانات الوسيطة حرية الاختباء في متغيرات محلية أو حقول كائن — بل تمر عبر الـ pipeline ضمن state صريح، بينما يبقى Action نفسه فارغًا بين الاستدعاءات.
$ uv run python examples/step_01_Action_and_pipeline/03_multiple_aspects.pyالقاموس المُعاد يستبدل بالكامل state — لا يُضيف إليه. @result_string("cleaned", required=True) هو مُتحقق: عقد قابل للتحقق على ناتج aspect. أخلّ بالوعد، وستوقِف الآلة الـ aspect فورًا، دون أن تدع state تالفة تمرّ أبعد من ذلك.
الوراثة
تُورَّث الـ Actions مثل الفئات العادية — باستثناء واحد متعمَّد: الـ aspects لا تُورَّث في الـ pipeline. تبني الآلة الـ pipeline فقط مما هو مُعلَن في الفئة نفسها.
$ uv run python examples/step_01_Action_and_pipeline/04_inheritance.pyيعمل ChildOrderAction بشكل ممتاز، لكن الـ pipeline الخاص به لا يحتوي إلا على child_summary — أما validate_aspect الخاص بالأصل فلا يُنفَّذ أبدًا. أما ExtendedOrderAction فيفعل كل شيء بشكل صحيح: فهو يُعلن الـ aspect من جديد ويستدعي super() للاستناد إلى منطق الأصل.
التجارب
تراكم في هذا الفصل عدد لا بأس به من القواعد — لكنها تشترك في خاصية واحدة: يُتحقَّق من معظمها عند إعلان الفئة، عند استيراد الوحدة، لا عند استدعاء الـ Action. الكود هنا مواصفة قابلة للتنفيذ؛ يظهر خرق العقد قبل أول تشغيل، لا في ليلة ما وسط بيئة الإنتاج. جرّب بنفسك — ابحث عن طريقة لكسر SayHelloAction من المثال الأول واضغط تشغيل.
$ uv run python examples/step_01_Action_and_pipeline/01_hello_world.pyالخلاصة
لقد تشكّل العمود الفقري للنموذج بالفعل: العملية هي Action ذو حد مُنمَّط (Params وResult)، وpipeline خطي من الـ aspects، وعقود يُتحقَّق من معظمها عند التهيئة. يُعبَّر عن السلوك في بنية الكود، لا في توثيق قائم بمعزل عنه.
أسئلة المراجعة
- أي ثابت يجعل الـ Action قابلاً للقراءة «من فئة واحدة»، وفي أي لحظة يُتحقَّق من ذلك؟
- لماذا يُعدّ غياب
@check_rolesخطأً، لا «إتاحة صامتة للجميع»؟ - الـ pipeline في AOA خطي — دون تفرعات أو مخارج جانبية. ماذا يمنحنا هذا القيد، وبأي ثمن؟
- لماذا لا تُورَّث الـ aspects في الـ pipeline تلقائيًا؟ قارن ذلك بالوراثة المعتادة للدوال في البرمجة الشيئية.
- ماذا تعني عبارة «الكود مواصفة قابلة للتنفيذ»، ولماذا يُتحقَّق من معظم العقود عند التهيئة لا في وقت التشغيل؟
