Software Architecture

تسلط بر معماری تمیز: ساخت سیستم‌های نرم‌افزاری قابل نگهداری

در دنیای مهندسی نرم‌افزار، مفاهیم کمی به اندازه معماری تمیز تحول‌آفرین هستند. این رویکرد معماری که توسط رابرت سی. مارتین (عموی باب) محبوب شد، نه تنها مجموعه‌ای از قوانین، بلکه فلسفه‌ای است که هدف آن آسان‌سازی تغییر، آزمایش و درک سیستم‌های نرم‌افزاری است. برای توسعه‌دهندگان سطح متوسط تا پیشرفته، تسلط بر این اصول برای موفقیت بلندمدت پروژه‌ها حیاتی است. این پست به بررسی اصول بنیادین معماری تمیز می‌پردازد و بر جداسازی دغدغه‌ها، قوانین وابستگی و استقلال منطق کسب‌وکار تمرکز دارد.

فلسفه اصلی: جداسازی دغدغه‌ها

در قلب معماری تمیز، جداسازی دغدغه‌ها قرار دارد. این معماری یک سیستم را به لایه‌هایی تقسیم می‌کند که هر کدام مسئولیت خاصی دارند. مهم‌ترین تمایز بین ارکان (Entities) و موارد استفاده (Use Cases) یا تعامل‌گرها در یک طرف، و مبدل‌های رابط و چارچوب‌ها/درایورها در طرف دیگر است. این ساختار تضمین می‌کند که منطق کسب‌وکار شما مستقل از چارچوب‌های خارجی، پایگاه داده‌ها یا فناوری‌های رابط کاربری باقی بماند.

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

قانون وابستگی و ساختار دایره‌ای

ویژگی تعریف‌کننده معماری تمیز، قانون وابستگی است: وابستگی‌های کد منبع فقط می‌توانند به سمت داخل اشاره کنند. این بدان معناست که فایل‌های موجود در دایره‌های داخلی (ارکان، موارد استفاده) نمی‌توانند هیچ چیزی درباره دایره‌های خارجی (کنترلرها، مبدل‌های رابط، چارچوب‌ها) بدانند.

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

تعریف اکان و موارد استفاده

بیایید به یک مثال عملی نگاه کنیم. فرض کنید سیستمی برای مدیریت حساب‌های کاربری دارید. در معماری تمیز، شما ابتدا ارکان خود را تعریف می‌کنید. این اکان شامل قوانین کسب‌وکار سراسری سازمان است و پایدارترین بخش برنامه شما محسوب می‌شود.

// entities/User.js
class User {
  constructor(id, name, email) {
    this.id = id;
    this.name = name;
    this.email = email;
  }

  // قانون کسب‌وکار: تنها در صورتی می‌توان ایمیل را تغییر داد که تأیید نشده باشد
  setEmail(newEmail) {
    if (this.isVerified) {
      throw new Error('کاربران تأیید شده نمی‌توانند ایمیل خود را بدون تأیید مجدد تغییر دهند');
    }
    this.email = newEmail;
  }

  verify() {
    this.isVerified = true;
  }
}

در مرحله بعد، مورد استفاده (یا تعامل‌گر) را تعریف می‌کنیم. این لایه جریان داده را به و از اکان‌ها برای انجام یک وظیفه خاص هماهنگ می‌کند. نکته کلیدی این است که مورد استفاده به انتزاعی از مخزن (Repository) وابسته است، نه به پیاده‌سازی عقی پایگاه داده.

// useCases/VerifyUser.js
class VerifyUserUseCase {
  constructor(userRepository) {
    this.userRepository = userRepository;
  }

  execute(userId) {
    const user = this.userRepository.findById(userId);
    if (!user) throw new Error('کاربر یافت نشد');
    
    user.verify(); // فراخوانی متد اکان
    this.userRepository.save(user); // فراخوانی انتزاع
  }
}

توجه داشته باشید که VerifyUserUseCase نمی‌داند که userRepository از SQL، NoSQL یا یک سیستم فایل استفاده می‌کند. او تنها می‌داند که یک شیء با متدهای findById و save وجود دارد. این استقلال است که آزمایش و نگهداری را به طور قابل توجهی آسان‌تر می‌کند.

پیاده‌سازی مبدل‌های رابط

لایه بیرونی، لایه مبدل‌های رابط، داده‌ها را از فرمتی که برای موارد استفاده و اکان‌ها مناسب‌تر است به فرمتی که برای عامل خارجی (مانند وب‌سرور یا پایگاه داده) مناسب‌تر است، ترجمه می‌کند. در اینجا است که شما رابط مخزن را که در لایه مورد استفاده تعریف شده است، پیاده‌سازی می‌کنید.

// adapters/InMemoryUserRepository.js
class InMemoryUserRepository {
  constructor() {
    this.users = [];
  }

  findById(id) {
    return this.users.find(u => u.id === id);
  }

  save(user) {
    const index = this.users.findIndex(u => u.id === user.id);
    if (index === -1) this.users.push(user);
    else this.users[index] = user;
  }
}

نتیجه‌گیری

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

Share: