Software Architecture

بازنویسی سیستم‌های تک‌قطبی با اصل مسئولیت واحد

سیستم‌های قدیمی اغلب از کلاس‌های متورم رنج می‌برند که نگرانی‌های بیش از حد را مدیریت می‌کنند. این ضدالگوی «شیء خداوندی» کد را برای تست، نگهداری و گسترش دشوار می‌کند. با اعمال اصل مسئولیت واحد (SRP)، می‌توانیم این سرویس‌های تک‌قطبی را بدون بازنویسی کل سیستم به واحدهای کوچک‌تر و متمرکزتر تقسیم کنیم. این راهنما یک نقشه راه عملی برای پیاده‌سازی SRP در محیط‌های تولید ارائه می‌دهد.

شناسایی نقض مسئولیت‌ها

گام اول شناسایی جایی است که SRP نقض شده است. یک کلاس دارای مسئولیت واحد است اگر تنها یک دلیل برای تغییر آن وجود داشته باشد. در کدهای قدیمی، این موضوع اغلب به صورت کلاسی ظاهر می‌شود که همزمان به دسترسی به پایگاه داده، منطق کسب‌وکار و قالب‌بندی رابط کاربری می‌پردازد.

به این نشانه‌ها در کدبیس موجود خود نگاه کنید:

  • کلاس‌هایی با وابستگی‌های بیش از حد به سایر اجزا.
  • متدهایی که چندین وظیفه نامرتبط را انجام می‌دهند (مثلاً اعتبارسنجی داده‌ها و ارسال ایمیل).
  • پیچیدگی سیکلوماتیک بالا، که نشان‌دهنده منطق گره‌خورده است.
  • تعارض‌های ادغام مکرر در فایل‌های خاص در طول توسعه مشارکتی.

گام 1: استخراج منطق دامنه

امن‌ترین راه برای شروع بازنویسی، استخراج منطق خالص کسب‌وکار از نگرانی‌های زیرساختی است. یک کلاس قدیمی `OrderProcessor` را در نظر بگیرید که همه چیز را از محاسبه کلیدها تا به‌روزرسانی پایگاه داده مدیریت می‌کند.


// نقض قدیمی: شیء خداوندی
public class OrderProcessor {
    public void processOrder(Order order) {
        // 1. منطق کسب‌وکار
        double tax = order.getTotal() * 0.1;
        order.setTotal(order.getTotal() + tax);

        // 2. نگرانی زیرساختی
        databaseConnection.update("orders", order);

        // 3. اثر جانبی
        emailService.sendConfirmation(order.getCustomerEmail());
    }
}

این کلاس به سه دلیل تغییر می‌کند: قوانین مالیات تغییر می‌کنند، طرح پایگاه داده تغییر می‌کند، یا ارائه‌دهندگان ایمیل تغییر می‌کنند. برای اعمال SRP، ما منطق محاسبه را در یک سرویس اختصاصی جدا می‌کنیم.

گام 2: پیاده‌سازی الگوی استراتژی برای قیمت‌گذاری

با استخراج محاسبه مالیات، ما یک رابط `TaxCalculator` ایجاد می‌کنیم. این کار منطق را ایزوله می‌کند و به ما اجازه می‌دهد پیاده‌سازی‌ها را بدون دست زدن به جریان اصلی سفارش تعویض کنیم.


// ساختار جدید مطابق با SRP
public interface TaxCalculator {
    double calculate(Order order);
}

public class StandardTaxCalculator implements TaxCalculator {
    public double calculate(Order order) {
        return order.getTotal() * 0.1;
    }
}

// پردازنده بازنویسی شده
public class OrderProcessor {
    private final TaxCalculator taxCalculator;
    private final OrderRepository repository;
    private final NotificationService notifier;

    public OrderProcessor(TaxCalculator taxCalculator, OrderRepository repo, NotificationService notifier) {
        this.taxCalculator = taxCalculator;
        this.repository = repo;
        this.notifier = notifier;
    }

    public void processOrder(Order order) {
        order.setTotal(order.getTotal() + taxCalculator.calculate(order));
        repository.save(order);
        notifier.sendConfirmation(order);
    }
}

گام 3: جداسازی نگرانی‌های زیرساختی

در مرحله بعد، اطمینان می‌کنیم که منطق ماندگاری (Persistence) و اعلان‌دهی به رابط‌ها واگذار شود. این کار فرآیند اصلی کسب‌وکار را از انتخاب‌های فناوری خاص جدا می‌کند. `OrderProcessor` اکنون فقط از انتزاعات آگاه است و به اصل وارونگی وابستگی پایبند است، که با نگه داشتن کلاس متمرکز بر هماهنگ‌سازی جریان کار، از SRP پشتیبانی می‌کند.

هنگام تست این پیاده‌سازی جدید، می‌توانید به راحتی `TaxCalculator` و `NotificationService` را شبیه‌سازی (Mock) کنید. قبلاً، تست واحد `OrderProcessor` قدیمی نیاز به راه‌اندازی پایگاه داده و سرور ایمیل داشت، که باعث می‌شد تست‌ها کند و ناپایدار باشند.

مدیریت گذار

بازنویسی یک تک‌قطبی بزرگ پرخطر است. برای کاهش این ریسک، از الگوی «درخت خفه‌کننده» (Strangler Fig) استفاده کنید. اجزای جدید و مطابق با SRP را پشت یک پرچم ویژگی (Feature Flag) معرفی کنید. به تدریج ترافیک را به پیاده‌سازی جدید هدایت کنید در حالی که کد قدیمی هنوز وجود دارد. زمانی که از پایداری ماژول‌های جدید اطمینان حاصل کردید، کد قدیمی را حذف کنید.

تست‌های خودکار در این فرآیند غیرقابل مذاکره هستند. قبل از دست زدن به هر کد قدیمی، تست‌های یکپارچه بنویسید که رفتار موجود را پوشش دهند. این تست‌ها به عنوان تور امانیت شما عمل می‌کنند و اطمینان می‌دهند که بازنویسی شما ساختار را تغییر می‌دهد، نه عملکرد را.

نتیجه‌گیری

اعمال اصل مسئولیت واحد به کد قدیمی درباره بازنویسی همه چیز از صفر نیست؛ بلکه درباره بهبود تدریجی است. با ایزوله کردن منطق کسب‌وکار، جداسازی نگرانی‌های زیرساختی و استفاده از الگوهای طراحی برای مدیریت وابستگی‌ها، می‌توانید تک‌قطبی‌های شکننده را به سرویس‌های قابل نگهداری تبدیل کنید. کوچک شروع کنید، به طور کامل تست کنید و به انضباط یک دلیل برای تغییر در هر کلاس پایبند باشید. نتیجه کدی است که برای سال‌های آینده درک، تست و گسترش آن آسان‌تر است.

Share: