Creating a Logical Architecture (การสร้าง Logical Architecture)
การสร้าง logical architecture เกี่ยวข้องกับการระบุและปรับโครงสร้าง logical component อย่างต่อเนื่อง Component identification ทำงานได้ดีที่สุดเมื่อเป็นกระบวนการทำซ้ำ มันเกี่ยวข้องกับการสร้าง component ตัวเลือก แล้วปรับแต่งผ่าน feedback loop ดังแสดงใน Figure 8-6
Figure 8-6. The component identification and refactoring cycle
กิจกรรมแรกที่สถาปนิกทำเมื่อพัฒนา logical architecture คือการระบุ core component เริ่มต้น แล้วกำหนด user story หรือ requirement ให้กับมัน หลังจากนั้นพวกเขาจะวิเคราะห์บทบาทและความรับผิดชอบของ component เพื่อให้แน่ใจว่า user story หรือ requirement ที่กำหนดให้นั้นสมเหตุสมผล จากนั้นสถาปนิกจะพิจารณา architectural characteristics ที่ระบบต้องรองรับ และตัดสินใจว่าต้อง refactor เพื่อแยกหรือรวม component เข้าด้วยกันตาม characteristics เหล่านั้นหรือไม่ สุดท้าย สถาปนิกอาจปรับแต่ง component ตามการวิเคราะห์นี้ กระบวนการนี้คือ feedback loop ที่แทบไม่หยุดเลย
workflow ใน Figure 8-6 สามารถใช้ได้กับระบบ greenfield (ระบบใหม่) หรือทุกครั้งที่มีการเพิ่มหรือเปลี่ยนฟีเจอร์ในระบบ ตัวอย่างเช่น สมมติคุณต้องทำให้ระบบ order-entry รองรับให้ผู้ใช้รับสินค้าที่ร้านได้แทนที่จะให้จัดส่งถึงบ้าน สิ่งนี้จะเกี่ยวข้องกับการเพิ่มโค้ดสำหรับการนัดหมาย รวมถึงการเปลี่ยนกระบวนการสั่งซื้อที่มีอยู่แล้ว การเปลี่ยนแปลงนี้อาจต้องใช้ component ใหม่ เปลี่ยนแปลง component เดิม หรือทั้งสองอย่าง เมื่อ component เปลี่ยนแปลง บทบาทและความรับผิดชอบของมันก็อาจเปลี่ยนตาม ซึ่งอาจกระตุ้นให้คุณต้องปรับโครงสร้างโค้ดหรือสร้าง component ใหม่
เราจะอธิบายแต่ละขั้นตอนเหล่านี้อย่างละเอียดในหัวข้อถัดไป
Identifying Core Components (การระบุ Core Component)
ความท้าทายในการเริ่มต้น logical architecture ใหม่ (หรือปรับปรุงของเดิมอย่างมาก) คือการกำหนดว่า core component เริ่มต้นควรเป็นอะไร ข้อผิดพลาดหนึ่งที่สถาปนิกซอฟต์แวร์หลายคนทำคือการทุ่มเทความพยายามมากเกินไปเพื่อให้ logical component เริ่มต้นสมบูรณ์แบบตั้งแต่ครั้งแรก แนวทางที่ดีกว่าคือการ "เดาที่ดีที่สุด" ว่า core component เริ่มต้นควรมีหน้าตาอย่างไร โดยอิงจากฟังก์ชันหลักของระบบ แล้วปรับแต่งผ่านขั้นตอน workflow ที่ระบุไว้ใน Figure 8-6 กล่าวอีกนัยหนึ่ง การทำซ้ำ (iterate) logical component ไปเรื่อย ๆ ตามที่คุณเรียนรู้เกี่ยวกับระบบมากขึ้นนั้นดีกว่าการพยายามทำให้สมบูรณ์แบบตั้งแต่ครั้งแรก ซึ่งเป็นตอนที่คุณรู้เกี่ยวกับระบบและ requirement เฉพาะเจาะจงน้อยที่สุด
ในกรณีส่วนใหญ่ สถาปนิกไม่จำเป็นต้องรู้ requirement และข้อกำหนดทั้งหมด (หรือบางครั้งก็ไม่ต้องรู้เลย) ของระบบก่อนเริ่มสร้าง logical component โดยทั่วไป core component เริ่มต้นจะอิงจากการกระทำหลักที่ผู้ใช้อาจทำ หรือ workflow การประมวลผลหลักของระบบ
เราชอบคิดว่า core component เริ่มต้นเป็นเหมือนถังเปล่า ถังนี้จะไม่ทำอะไรเลยจนกว่าสถาปนิกจะเริ่ม "เติม" มัน — นั่นคือกำหนด user story หรือ requirement ให้กับ component นั้น ดังแสดงใน Figure 8-7 ชื่อของ component ควรสะท้อนบทบาทและความรับผิดชอบที่เสนอไว้ อย่างไรก็ตาม จนกว่าสถาปนิกจะกำหนด user story ให้กับมัน (ขั้นตอนถัดไปใน workflow) มันก็ยังคงเป็นเพียง placeholder — การเดาที่ดีที่สุดสำหรับหน่วยการทำงาน (functional building block)
Figure 8-7. In the beginning, initial components are like empty buckets
ต่อไปนี้คือแนวทางทั่วไปสามแบบสำหรับการสร้าง core component เริ่มต้น สองแบบแรก (Workflow และ Actor/Action) เป็นแนวทางที่เราพบว่ามีประโยชน์ ส่วนแบบที่สาม (Entity Trap) เป็น antipattern ที่เราขอเตือนให้ระวัง
The Workflow approach (แนวทางแบบ Workflow)
แนวทางทั่วไปที่สถาปนิกใช้ในการระบุ core component เริ่มต้นของ logical architecture คือแนวทาง Workflow ตามชื่อที่บอกไว้ แนวทางนี้อาศัย happy-path workflow หลัก (ที่ไม่มี error) ที่ผู้ใช้อาจดำเนินการผ่านระบบ (หรือ workflow การประมวลผล request หลักของระบบ) ถ้าสถาปนิกมีความเข้าใจ flow โดยทั่วไปอยู่บ้าง พวกเขาก็สามารถพัฒนา component จากขั้นตอนเหล่านั้นได้
ตัวอย่างเช่น สมมติคุณกำลังสร้างระบบ order-entry ใหม่ให้บริษัทของคุณ คุณยังไม่รู้ requirement และข้อกำหนดเฉพาะเจาะจง แต่อย่างน้อยคุณก็รู้ workflow ทั่วไปสำหรับการประมวลผลคำสั่งซื้อใหม่ คุณจึงสามารถกำหนด component ให้กับแต่ละขั้นตอนใน workflow ได้ดังนี้
-
ผู้ใช้เรียกดูแคตตาล็อกสินค้า →
Item Browser -
ผู้ใช้สั่งซื้อ →
Order Placement -
ผู้ใช้ชำระเงินสำหรับคำสั่งซื้อ →
Order Payment -
ส่งอีเมลรายละเอียดคำสั่งซื้อให้ผู้ใช้ →
Customer Notification -
เตรียมคำสั่งซื้อ →
Order Fulfillment -
จัดส่งคำสั่งซื้อ →
Order Shipment -
ส่งอีเมลแจ้งลูกค้าว่าคำสั่งซื้อถูกจัดส่งแล้ว →
Customer Notification -
ติดตามการจัดส่ง →
Order Tracking
ในแนวทาง Workflow ไม่ใช่ทุกขั้นตอนใน workflow หลักจะให้ component ใหม่เสมอไป ใน workflow ข้างต้น ขั้นตอนที่ 4 และ 7 ทั้งคู่ใช้ component Customer Notification สถาปนิกจะสร้างโมเดลของ workflow หลักหรือ user journey ให้ได้มากที่สุดเท่าที่จะทำได้สำหรับระบบ แล้วระบุ component ที่สอดคล้องกับขั้นตอนเหล่านั้น
นี่ก็ยังเป็นแค่ "การเดาที่ดีที่สุด" ว่า logical architecture จะมีหน้าตาอย่างไร component เหล่านี้แทนถังเปล่าและยังไม่มีความรับผิดชอบใด ๆ ดังนั้นพวกมันจึงมีแนวโน้มจะเปลี่ยนแปลงไปเมื่อวิวัฒนาการต่อ (ดังแสดงใน Figure 8-6 ) นี่เป็นเรื่องปกติอย่างยิ่งและเป็นส่วนหนึ่งของธรรมชาติการทำซ้ำของ software architecture อย่ากังวลกับการพยายามสร้างโมเดลทุก workflow ในระบบของคุณ แต่ให้โฟกัสที่ workflow หลัก ส่วนที่เหลือจะวิวัฒนาการไปเองเมื่อคุณเรียนรู้เกี่ยวกับระบบมากขึ้นและเริ่มรวบรวม user story และ requirement
The Actor/Action approach (แนวทางแบบ Actor/Action)
อีกวิธีหนึ่งที่สถาปนิกใช้ระบุ core component เริ่มต้นคือแนวทาง Actor/Action แนวทางนี้มีประโยชน์อย่างยิ่งเมื่อระบบมี actor หลายตัว ด้วยแนวทางนี้ สถาปนิกจะระบุการกระทำหลักที่ผู้ใช้สามารถทำได้ในระบบ (เช่น การสั่งซื้อ) ตัวระบบเองก็ถือเป็น actor เสมอเช่นกัน โดยทำหน้าที่อัตโนมัติ เช่น การออกใบแจ้งหนี้และการเติมสต็อก
ในตัวอย่างระบบ order-entry ของเรา สถาปนิกอาจระบุ actor สามตัว คือ customer , order packer (คนที่แพ็คกล่องและส่งไปจัดส่ง) และ system จากนั้นสถาปนิกจะระบุการกระทำหลักที่แต่ละ actor เหล่านี้ทำ และกำหนด component ให้กับการกระทำเหล่านั้น
Customer actor
-
ค้นหาสินค้า →
Item Search -
ดูรายละเอียดสินค้า →
Item Details -
สั่งซื้อ →
Order Placement -
ยกเลิกคำสั่งซื้อ →
Order Cancel -
ลงทะเบียนเป็นลูกค้าใหม่ →
Customer Registration -
อัปเดตข้อมูลลูกค้า →
Customer Profile
Order packer actor
-
เลือกขนาดกล่อง →
Order Fulfillment -
ทำเครื่องหมายว่าคำสั่งซื้อพร้อมจัดส่งแล้ว →
Order Fulfillment -
จัดส่งคำสั่งซื้อให้ลูกค้า →
Order Shipment
System actor
-
ปรับสินค้าคงคลัง →
Inventory Management -
สั่งสต็อกเพิ่มจากซัพพลายเออร์ →
Supplier Ordering -
ดำเนินการชำระเงิน →
Order Payment
เช่นเดียวกับแนวทาง Workflow ไม่ใช่ทุกการกระทำจะมี component ของตัวเองเสมอไป ตัวอย่างเช่น การเลือกขนาดกล่องสำหรับคำสั่งซื้อและการทำเครื่องหมายว่าพร้อมจัดส่งนั้นทำโดย component Order Fulfillment เดียวกัน
โดยทั่วไป แนวทาง Actor/Action มักสร้าง component มากกว่าแนวทาง Workflow ขึ้นอยู่กับว่าสถาปนิกเลือกสร้างโมเดล workflow หลักกี่ตัว แต่ทั้งสองแนวทาง สถาปนิกสามารถระบุ core component เริ่มต้นและวิธีที่พวกมันสื่อสารกันได้ แม้ก่อนที่จะได้รับ requirement หรือข้อกำหนดโดยละเอียด
The Entity Trap (กับดัก Entity)
มันน่าดึงดูดเกินไปสำหรับสถาปนิกที่จะเริ่มระบุ component โดยโฟกัสที่ entity ที่เกี่ยวข้องกับระบบ แล้วจึงสร้าง component จาก entity เหล่านั้น ตัวอย่างเช่น ในระบบ order entry ทั่วไป คุณอาจระบุ Customer , Item และ Order เป็น entity หลักในระบบ แล้วสร้าง component Customer Manager , component Item Manager และ component Order Manager ตามลำดับ เราขอแนะนำอย่างยิ่งให้หลีกเลี่ยงแนวทางนี้ด้วยเหตุผลต่อไปนี้
ประการแรก ชื่อของ logical component เหล่านี้กำกวมและไม่ได้อธิบายบทบาทของ component ตัวอย่างเช่น การถามว่า component Order Manager ทำอะไรโดยดูจากชื่ออย่างเดียวจะได้คำตอบที่ไร้ประโยชน์ว่า "มันจัดการคำสั่งซื้อ" ซึ่งไม่บอกอะไรเลยเกี่ยวกับบทบาทหรือความรับผิดชอบเฉพาะของมันในระบบ เทียบกับชื่อ component อย่าง Validate Order แล้ว ความสำคัญของชื่อ component ที่ดีและสื่อความหมายชัดเจนก็ปรากฏชัด ถ้าชื่อ component มีคำต่อท้ายอย่าง Manager , Supervisor , Controller , Handler , Engine หรือ Processor นั่นเป็นสัญญาณที่ดีว่าสถาปนิกอาจกำลังติดกับ antipattern ที่เรียกว่า Entity Trap
ประการที่สอง component จะกลายเป็นที่ทิ้งขยะของฟังก์ชันการทำงานที่เกี่ยวข้องกับ domain ลองพิจารณาชื่อ component ที่อิง entity อย่าง Order Manager ดังแสดงใน Figure 8-8 ฟังก์ชันการทำงานเกี่ยวกับคำสั่งซื้อทุกชิ้นจะถูกยัดเข้าไปใน component เดียวนั้น ไม่ว่าจะเป็นการตรวจสอบคำสั่งซื้อ การสั่งซื้อ ประวัติคำสั่งซื้อ การเตรียมคำสั่งซื้อ การจัดส่งคำสั่งซื้อ การติดตามคำสั่งซื้อ และอื่น ๆ โดยพื้นฐานแล้วมันจะกลายเป็นเหมือนคลาส utility แบบ "ยัดทุกอย่างลงไป" ที่นักพัฒนาทุกคนเคยเขียนอย่างน้อยครั้งหนึ่งในอาชีพของตน ที่มีเมท็อดนับสิบตัวสำหรับจัดการสตริง จัดการข้อมูล คำนวณเวลา และอะไรก็ตามที่นักพัฒนาจะยัดลงไปได้
ประการที่สาม เมื่อ component มี granularity ที่หยาบเกินไป มันจะทำงานมากเกินไปและสูญเสียจุดประสงค์ของตัวเอง แทนที่จะมี granularity ละเอียดและจุดประสงค์เดียว (เหมือนตัวอย่าง component Validate Order ) component อาจใหญ่เกินไป component แบบนี้ดูแลรักษา ทดสอบ และ deploy ได้ยาก จึงไม่ค่อยน่าเชื่อถือ
Figure 8-8. Using the Entity Trap antipattern produces components with too much responsibility
ถ้าสถาปนิกกำลังสร้างระบบที่เป็นแบบ entity based อย่างแท้จริง และแค่ทำงาน CRUD (create, read, update, delete) กับ entity เหล่านั้น ระบบก็ไม่จำเป็นต้องมี architecture เลย แต่ควรใช้ framework, เครื่องมือ, หรือสภาพแวดล้อม no-code/low-code ที่อิงกับ CRUD ซึ่งให้นักพัฒนา generate ซอร์สโค้ดส่วนใหญ่ที่ทำงานกับ entity เหล่านั้นแทน
Assigning User Stories to Components (การกำหนด User Story ให้กับ Component)
ขั้นตอนถัดไปในการสร้าง logical architecture คือการกำหนด user story หรือ requirement ให้กับ logical component นี่เป็นกระบวนการทำซ้ำ เพราะ user story หรือ requirement ส่วนใหญ่ไม่ได้ถูกรู้ทั้งหมดตั้งแต่แรก พวกมันวิวัฒนาการไปพร้อมกับระบบ ขั้นตอนนี้มีจุดมุ่งหมายเพื่อเริ่มเติมถังเปล่าเหล่านั้น มอบบทบาทและความรับผิดชอบที่ชัดเจนให้กับ component ดังแสดงใน Figure 8-9
Figure 8-9. Filling up the empty buckets (components) with user stories or requirements
เพื่อดูว่า logical component วิวัฒนาการอย่างไร ลองพิจารณา user story ต่อไปนี้
Customer #1
ในฐานะลูกค้า ผมอยากให้คำสั่งซื้อของผมถูกตรวจสอบเพื่อให้แน่ใจว่าผมกรอกข้อมูลครบถ้วนและถูกต้อง
Order preparer
ในฐานะคนเตรียมคำสั่งซื้อ ผมอยากรู้ว่าควรใช้กล่องขนาดไหนสำหรับคำสั่งซื้อนี้ เพื่อจะได้แพ็คได้อย่างมีประสิทธิภาพที่สุด
Customer #2
ในฐานะลูกค้า ผมอยากได้รับอีเมลทุกครั้งที่สถานะคำสั่งซื้อเปลี่ยนแปลง เพื่อให้ผมรู้สถานะคำสั่งซื้อของตัวเองอยู่เสมอ
สมมติว่าสถาปนิกได้ระบุ logical component ต่อไปนี้ไว้แล้ว
-
Order Placement -
Order Fulfillment -
Order Shipment -
Inventory Management
การกำหนด user story แรกให้กับ component Order Placement นั้นสมเหตุสมผล เพราะนั่นคือ component ที่ผู้ใช้กำลังปฏิสัมพันธ์ด้วยเพื่อสั่งซื้อ
ตรวจสอบคำสั่งซื้อ (user story ของ Customer #1) →
Order Placement
การกำหนดขนาดกล่องน่าจะควรจัดการโดย component Order Fulfillment เพราะมันรับผิดชอบตรรกะทั้งหมดของระบบที่จำเป็นในการเตรียมและแพ็คคำสั่งซื้อลงกล่อง
กำหนดขนาดกล่อง (user story ของ Order preparer) →
Order Fulfillment
แต่แล้ว user story ที่สามล่ะ component ไหนในสี่ตัวที่ระบุไว้ควรส่งอีเมลให้ลูกค้าเมื่อคำสั่งซื้อถูกสั่ง พร้อมจัดส่ง และถูกจัดส่งแล้ว คำตอบอาจเป็น component Order Placement , Order Fulfillment และ Order Shipment แต่โปรดจำไว้ว่า user story ถูก implement ผ่านซอร์สโค้ด ซึ่งต้องอยู่ในไดเรกทอรีหรือ namespace เฉพาะ เนื่องจากการทำโค้ดซ้ำใน component ทั้งสามตัวไม่ใช่ความคิดที่ดี สถาปนิกจึงต้องนิยาม component ใหม่เพื่อจัดการ user story นี้
ส่งอีเมลถึงลูกค้า (user story ของ Customer #2) →
Customer Notification(ใหม่)
component Order Placement , Order Fulfillment และ Order Shipment ต้องสื่อสารกับ component ใหม่นี้เพื่อแจ้งให้มันส่งอีเมล เมื่อเพิ่มส่วนนี้เข้าไป logical architecture ก็จะมีหน้าตาเป็นแบบ Figure 8-10
Figure 8-10. Evolving components based on new user stories
Analyzing Roles and Responsibilities (การวิเคราะห์บทบาทและความรับผิดชอบ)
ขั้นตอนถัดไปในการปรับแต่ง logical component คือการวิเคราะห์บทบาทและความรับผิดชอบของแต่ละ component นี่คือวิธีที่สถาปนิกยืนยันว่า requirement หรือ user story ที่กำหนดให้กับ component เหล่านั้นเหมาะสมจริง และ component ไม่ได้ทำงานมากเกินไป สิ่งที่สถาปนิกกังวลในขั้นตอนนี้คือ cohesion นั่นคือ การทำงานของ component เกี่ยวโยงกันอย่างไรและมากแค่ไหน เมื่อเวลาผ่านไป component อาจใหญ่เกินไปได้ แม้ว่าการทำงานทั้งหมดของมันจะยังเกี่ยวโยงกันอยู่ก็ตาม
เพื่อแสดงให้เห็นว่าขั้นตอนนี้ทำงานอย่างไร สมมติสถาปนิกได้กำหนด requirement ต่อไปนี้ให้กับ component Order Placement
-
ตรวจสอบคำสั่งซื้อเพื่อให้แน่ใจว่าทุกฟิลด์ถูกกรอกและถูกต้อง
-
แสดงตะกร้าสินค้าพร้อมคำอธิบาย จำนวน และราคาของสินค้า
-
กำหนดที่อยู่จัดส่งที่ถูกต้อง
-
เก็บข้อมูลการชำระเงิน
-
สร้างหมายเลขคำสั่งซื้อที่ไม่ซ้ำกัน
-
ดำเนินการชำระเงินสำหรับคำสั่งซื้อ
-
ปรับจำนวนสินค้าคงคลังสำหรับสินค้าที่สั่งซื้อ
-
ส่งอีเมลสรุปคำสั่งซื้อให้ลูกค้า
ถ้าสถาปนิกจะเขียนข้อความบทบาทและความรับผิดชอบสำหรับ component นี้ มันจะอ่านได้ดังนี้
component นี้รับผิดชอบการตรวจสอบคำสั่งซื้อและแสดงตะกร้าสินค้าที่ถูกต้อง พร้อมรูปภาพสินค้า คำอธิบาย จำนวน และราคา component นี้ยังรับผิดชอบการกำหนดที่อยู่จัดส่งที่ถูกต้องสำหรับคำสั่งซื้อ รวมถึงเก็บข้อมูลการชำระเงินทั้งหมดจากลูกค้า นอกจากนี้ยังรับผิดชอบการดำเนินการชำระเงิน ปรับสินค้าคงคลัง และส่งอีเมลสรุปคำสั่งซื้อให้ลูกค้าด้วย
แม้ว่าการทำงานทั้งหมดนี้จะเกี่ยวข้องกับการสั่งซื้อ แต่ component Order Placement ก็เห็นได้ชัดว่ารับผิดชอบมากเกินไป โดยเฉพาะเรื่องการดำเนินการชำระเงิน การปรับสินค้าคงคลัง และการส่งอีเมลให้ลูกค้า วิธีหนึ่งในการดูว่า component รับผิดชอบมากเกินไปหรือไม่คือการมองหาคำเชื่อมประโยคอย่าง และ , ยังรวมถึง , นอกจากนี้ หรือ เช่นเดียวกับ และการใช้เครื่องหมายจุลภาคมากเกินไป
จำได้ไหมว่าในตอนต้นของบทนี้เราบอกว่า logical component แทนด้วย namespace หรือไดเรกทอรีใน code repository ในกรณีของ component Order Placement ซอร์สโค้ด ทั้งหมด ที่แทน component นี้จะอยู่ในไดเรกทอรีหรือ namespace เดียวกัน เช่น com/app/order/placement หรือ com.app.order.placement นี่เป็นฟังก์ชันการทำงานจำนวนมาก — น่าจะเป็นโค้ดที่มากเกินไปสำหรับไดเรกทอรีเดียว ดังนั้นจึงสมเหตุสมผลที่จะแยกไฟล์คลาสสำหรับการประมวลผลการชำระเงิน การจัดการสินค้าคงคลัง และการสื่อสารทางอีเมล ออกเป็นไดเรกทอรีแยกต่างหากที่แทนฟังก์ชันการทำงานเหล่านั้น นี่แหละคือสิ่งที่การแยก logical component หมายถึงจริง ๆ
ถ้าสถาปนิกย้ายความรับผิดชอบเรื่องการดำเนินการชำระเงิน การปรับสินค้าคงคลัง และการส่งอีเมลให้ลูกค้าไปเป็น component แยกต่างหาก พวกเขาก็สามารถลดความรับผิดชอบของ component Order Placement ตัวเดียวลงได้ ทำให้ดูแลรักษา ทดสอบ และ deploy ได้ง่ายขึ้น component ที่ได้จะมีหน้าตาดังนี้
Order Placement
-
ตรวจสอบคำสั่งซื้อเพื่อให้แน่ใจว่าทุกฟิลด์ถูกกรอกและถูกต้อง
-
แสดงตะกร้าสินค้าพร้อมคำอธิบาย จำนวน และราคาของสินค้า
-
กำหนดที่อยู่จัดส่งที่ถูกต้อง
-
เก็บข้อมูลการชำระเงิน
-
สร้างหมายเลขคำสั่งซื้อที่ไม่ซ้ำกัน
Payment Processing
- ดำเนินการชำระเงิน
Inventory Management
- ปรับจำนวนสินค้าคงคลังสำหรับสินค้าที่สั่งซื้อ
Customer Notification
- ส่งอีเมลสรุปคำสั่งซื้อให้ลูกค้า
ตอนนี้แต่ละ component มีบทบาทและความรับผิดชอบที่ชัดเจนและแตกต่างกันมากขึ้น
Analyzing Architectural Characteristics (การวิเคราะห์ Architectural Characteristics)
ขั้นตอนวิเคราะห์สุดท้ายคือการพิจารณา architectural characteristics ที่ระบบต้องการ architectural characteristics บางตัว เช่น scalability, reliability, availability, fault tolerance, elasticity และ agility (ความสามารถในการตอบสนองต่อการเปลี่ยนแปลงได้อย่างรวดเร็ว) อาจส่งผลต่อขนาดของ logical component
ตัวอย่างเช่น การแยก component ขนาดใหญ่ (ที่มีความรับผิดชอบมาก) ออกเป็น component ที่เล็กลงทำให้แต่ละตัวดูแลรักษาและทดสอบได้ง่ายขึ้น (นั่นคือ agility) และให้ scalability, elasticity และ fault tolerance ที่ดีกว่า อีกตัวอย่างที่ดีคือกรณีที่สองส่วนของระบบจัดการกับ input ของผู้ใช้ ถ้าส่วนหนึ่งจัดการผู้ใช้พร้อมกันหลายร้อยคน ในขณะที่อีกส่วนต้องรองรับแค่ไม่กี่คนในแต่ละครั้ง ทั้งสองส่วนก็จะต้องการ architecture characteristics ที่ต่างกัน ดังนั้น แม้มุมมองเชิงฟังก์ชันล้วน ๆ ของการออกแบบ component อาจนำสถาปนิกไปสู่การกำหนด component เดียวเพื่อจัดการปฏิสัมพันธ์กับผู้ใช้ แต่การวิเคราะห์ component ในแง่ของ architecture characteristics อาจนำไปสู่การแบ่งย่อยแทน
เพราะสถาปนิกต้องรู้ architectural characteristics ก่อนสร้าง logical architecture โดยทั่วไปจึงทำกันทีหลัง after การกำหนดว่า architectural characteristics ใดสำคัญที่สุดต่อระบบ
Restructuring Components (การปรับโครงสร้าง Component)
Feedback มีความสำคัญอย่างยิ่งในการออกแบบซอฟต์แวร์ สถาปนิกต้อง iterate การออกแบบ component ของตนอย่างต่อเนื่องร่วมกับนักพัฒนา การออกแบบซอฟต์แวร์นั้นมาพร้อมความยากลำบากที่ไม่คาดคิดสารพัดแบบ ดังนั้น แนวทางแบบทำซ้ำในการออกแบบ component จึงสำคัญมาก ประการแรก แทบเป็นไปไม่ได้ที่จะคาดการณ์การค้นพบและ edge case ทั้งหมดที่จะเกิดขึ้น ซึ่งแต่ละอย่างอาจกระตุ้นให้ต้องออกแบบใหม่ ประการที่สอง เมื่อ architecture และนักพัฒนาเจาะลึกเข้าไปในการสร้างแอปพลิเคชันมากขึ้น พวกเขาก็จะเข้าใจอย่างละเอียดมากขึ้นว่าพฤติกรรมและบทบาทต่าง ๆ ควรอยู่ที่ไหน
สถาปนิกควรคาดหวังว่าจะต้องปรับโครงสร้าง component บ่อยครั้งตลอดวงจรชีวิตของระบบหรือผลิตภัณฑ์ — ไม่ใช่แค่ในระบบ greenfield เท่านั้น แต่ในทุกระบบที่มีการดูแลรักษาบ่อย ๆ