فارسی

یاد بگیرید که چگونه معماری شش ضلعی (پورت‌ها و آداپتورها)، قابلیت نگهداری، تست‌پذیری و انعطاف‌پذیری برنامه‌های شما را بهبود می‌بخشد. این راهنما شامل مثال‌های عملی و بینش‌های کاربردی است.

معماری شش ضلعی: یک راهنمای عملی برای پورت‌ها و آداپتورها

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

معماری شش ضلعی چیست؟

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

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

اصول کلیدی معماری شش ضلعی

چندین اصل کلیدی، اثربخشی معماری شش ضلعی را تضمین می‌کنند:

مزایای استفاده از معماری شش ضلعی

اتخاذ معماری شش ضلعی مزایای متعددی را ارائه می‌دهد:

پیاده‌سازی معماری شش ضلعی: یک مثال عملی

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

۱. تعریف هسته (Application)

هسته برنامه شامل منطق کسب‌وکار برای ثبت‌نام یک کاربر جدید است.


// Core/UserService.java (یا UserService.cs)
public class UserService {
    private final UserRepository userRepository;
    private final PasswordHasher passwordHasher;
    private final UserValidator userValidator;

    public UserService(UserRepository userRepository, PasswordHasher passwordHasher, UserValidator userValidator) {
        this.userRepository = userRepository;
        this.passwordHasher = passwordHasher;
        this.userValidator = userValidator;
    }

    public Result<User, String> registerUser(String username, String password, String email) {
        // اعتبارسنجی ورودی کاربر
        ValidationResult validationResult = userValidator.validate(username, password, email);
        if (!validationResult.isValid()) {
            return Result.failure(validationResult.getErrorMessage());
        }

        // بررسی اینکه آیا کاربر از قبل وجود دارد
        if (userRepository.findByUsername(username).isPresent()) {
            return Result.failure("Username already exists");
        }

        // هش کردن رمز عبور
        String hashedPassword = passwordHasher.hash(password);

        // ایجاد کاربر جدید
        User user = new User(username, hashedPassword, email);

        // ذخیره کاربر در مخزن
        userRepository.save(user);

        return Result.success(user);
    }
}

۲. تعریف پورت‌ها

ما پورت‌هایی را که هسته برنامه برای تعامل با دنیای خارج استفاده می‌کند، تعریف می‌کنیم.


// Ports/UserRepository.java (یا UserRepository.cs)
public interface UserRepository {
    Optional<User> findByUsername(String username);
    void save(User user);
}

// Ports/PasswordHasher.java (یا PasswordHasher.cs)
public interface PasswordHasher {
    String hash(String password);
}

//Ports/UserValidator.java (یا UserValidator.cs)
public interface UserValidator{
  ValidationResult validate(String username, String password, String email);
}

//Ports/ValidationResult.java (یا ValidationResult.cs)
public interface ValidationResult{
  boolean isValid();
  String getErrorMessage();
}

۳. تعریف آداپتورها

ما آداپتورهایی را پیاده‌سازی می‌کنیم که هسته برنامه را به فناوری‌های خاص متصل می‌کنند.


// Adapters/DatabaseUserRepository.java (یا DatabaseUserRepository.cs)
public class DatabaseUserRepository implements UserRepository {
    private final DatabaseConnection databaseConnection;

    public DatabaseUserRepository(DatabaseConnection databaseConnection) {
        this.databaseConnection = databaseConnection;
    }

    @Override
    public Optional<User> findByUsername(String username) {
        // پیاده‌سازی با استفاده از JDBC، JPA یا فناوری دسترسی به پایگاه داده دیگر
        // ...
        return Optional.empty(); // مقدار جایگزین
    }

    @Override
    public void save(User user) {
        // پیاده‌سازی با استفاده از JDBC، JPA یا فناوری دسترسی به پایگاه داده دیگر
        // ...
    }
}

// Adapters/BCryptPasswordHasher.java (یا BCryptPasswordHasher.cs)
public class BCryptPasswordHasher implements PasswordHasher {
    @Override
    public String hash(String password) {
        // پیاده‌سازی با استفاده از کتابخانه BCrypt
        // ...
        return "hashedPassword"; //مقدار جایگزین
    }
}

//Adapters/SimpleUserValidator.java (یا SimpleUserValidator.cs)
public class SimpleUserValidator implements UserValidator {
  @Override
  public ValidationResult validate(String username, String password, String email){
    //منطق اعتبارسنجی ساده
     if (username == null || username.isEmpty()) {
            return new SimpleValidationResult(false, "Username cannot be empty");
        }
        if (password == null || password.length() < 8) {
            return new SimpleValidationResult(false, "Password must be at least 8 characters long");
        }
        if (email == null || !email.contains("@")) {
            return new SimpleValidationResult(false, "Invalid email format");
        }

        return new SimpleValidationResult(true, null);
  }
}

//Adapters/SimpleValidationResult.java (یا SimpleValidationResult.cs)
public class SimpleValidationResult implements ValidationResult {
  private final boolean valid;
  private final String errorMessage;

    public SimpleValidationResult(boolean valid, String errorMessage) {
        this.valid = valid;
        this.errorMessage = errorMessage;
    }
  @Override
  public boolean isValid(){
    return valid;
  }

  @Override
  public String getErrorMessage(){
    return errorMessage;
  }
}



//Adapters/WebUserController.java (یا WebUserController.cs)
// آداپتور راه‌انداز - درخواست‌های وب را مدیریت می‌کند
public class WebUserController {
    private final UserService userService;

    public WebUserController(UserService userService) {
        this.userService = userService;
    }

    public String registerUser(String username, String password, String email) {
        Result<User, String> result = userService.registerUser(username, password, email);
        if (result.isSuccess()) {
            return "Registration successful!";
        } else {
            return "Registration failed: " + result.getFailure();
        }
    }
}


۴. ترکیب‌بندی (Composition)

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


// کلاس اصلی یا پیکربندی تزریق وابستگی
public class Main {
    public static void main(String[] args) {
        // ایجاد نمونه‌هایی از آداپتورها
        DatabaseConnection databaseConnection = new DatabaseConnection("jdbc:mydb://localhost:5432/users", "user", "password");
        DatabaseUserRepository userRepository = new DatabaseUserRepository(databaseConnection);
        BCryptPasswordHasher passwordHasher = new BCryptPasswordHasher();
        SimpleUserValidator userValidator = new SimpleUserValidator();

        // ایجاد نمونه‌ای از هسته برنامه، با تزریق آداپتورها
        UserService userService = new UserService(userRepository, passwordHasher, userValidator);

        // ایجاد یک آداپتور راه‌انداز و اتصال آن به سرویس
        WebUserController userController = new WebUserController(userService);

        // اکنون می‌توانید درخواست‌های ثبت‌نام کاربر را از طریق userController مدیریت کنید
        String result = userController.registerUser("john.doe", "P@sswOrd123", "john.doe@example.com");
        System.out.println(result);
    }
}



// DatabaseConnection یک کلاس ساده فقط برای اهداف نمایشی است
class DatabaseConnection {
    private String url;
    private String username;
    private String password;

    public DatabaseConnection(String url, String username, String password) {
        this.url = url;
        this.username = username;
        this.password = password;
    }

    // ... متدهایی برای اتصال به پایگاه داده (برای اختصار پیاده‌سازی نشده است)
}

// کلاس Result (مشابه Either در برنامه‌نویسی تابعی)
class Result<T, E> {
    private final T success;
    private final E failure;
    private final boolean isSuccess;

    private Result(T success, E failure, boolean isSuccess) {
        this.success = success;
        this.failure = failure;
        this.isSuccess = isSuccess;
    }

    public static <T, E> Result<T, E> success(T value) {
        return new Result<>(value, null, true);
    }

    public static <T, E> Result<T, E> failure(E error) {
        return new Result<>(null, error, false);
    }

    public boolean isSuccess() {
        return isSuccess;
    }

    public T getSuccess() {
        if (!isSuccess) {
            throw new IllegalStateException("Result is a failure");
        }
        return success;
    }

    public E getFailure() {
        if (isSuccess) {
            throw new IllegalStateException("Result is a success");
        }
        return failure;
    }
}

class User {
    private String username;
    private String password;
    private String email;

    public User(String username, String password, String email) {
        this.username = username;
        this.password = password;
        this.email = email;
    }

    // getterها و setterها (برای اختصار حذف شده‌اند)

}

توضیحات:

ملاحظات پیشرفته و بهترین شیوه‌ها

در حالی که اصول اولیه معماری شش ضلعی ساده هستند، برخی ملاحظات پیشرفته برای به خاطر سپردن وجود دارد:

مثال‌های دنیای واقعی از کاربرد معماری شش ضلعی

بسیاری از شرکت‌ها و پروژه‌های موفق معماری شش ضلعی را برای ساخت سیستم‌های قوی و قابل نگهداری به کار گرفته‌اند:

چالش‌ها و بده‌بستان‌ها

در حالی که معماری شش ضلعی مزایای قابل توجهی ارائه می‌دهد، مهم است که چالش‌ها و بده‌بستان‌های موجود را نیز در نظر بگیریم:

ارزیابی دقیق مزایا و چالش‌های معماری شش ضلعی در چارچوب نیازمندی‌های خاص پروژه و توانایی‌های تیم شما بسیار مهم است. این یک راه‌حل جادویی نیست و ممکن است برای هر پروژه‌ای بهترین انتخاب نباشد.

نتیجه‌گیری

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

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

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