Eesti

Õppige, kuidas heksagonaalne arhitektuur, tuntud ka kui portide ja adapterite muster, võib parandada teie rakenduste hooldatavust, testitavust ja paindlikkust. See juhend pakub praktilisi näiteid ja kasulikke teadmisi arendajatele üle maailma.

Heksagonaalne arhitektuur: praktiline juhend portide ja adapterite kohta

Pidevalt arenevas tarkvaraarenduse maastikul on vastupidavate, hooldatavate ja testitavate rakenduste loomine esmatähtis. Heksagonaalne arhitektuur, tuntud ka kui portide ja adapterite muster, on arhitektuurimuster, mis tegeleb nende probleemidega, eraldades rakenduse tuumik-äriloogika selle välistest sõltuvustest. Selle juhendi eesmärk on anda põhjalik ülevaade heksagonaalsest arhitektuurist, selle eelistest ja praktilistest rakendusstrateegiatest arendajatele üle maailma.

Mis on heksagonaalne arhitektuur?

Heksagonaalne arhitektuur, mille mõiste lõi Alistair Cockburn, keskendub rakenduse tuumik-äriloogika eraldamisele välismaailmast. See eraldamine saavutatakse portide ja adapterite abil.

Mõelge sellest nii: rakenduse tuumik asub keskel, ümbritsetuna heksagonaalsest kestast. Pordid on selle kesta sisenemis- ja väljumispunktid ning adapterid ühendatakse nendesse portidesse, ühendades tuumiku välismaailmaga.

Heksagonaalse arhitektuuri põhiprintsiibid

Heksagonaalse arhitektuuri tõhususe aluseks on mitu põhiprintsiipi:

Heksagonaalse arhitektuuri kasutamise eelised

Heksagonaalse arhitektuuri kasutuselevõtt pakub mitmeid eeliseid:

Heksagonaalse arhitektuuri rakendamine: praktiline näide

Illustreerime heksagonaalse arhitektuuri rakendamist lihtsustatud näitega kasutaja registreerimissüsteemist. Kasutame selguse huvides hüpoteetilist programmeerimiskeelt (sarnane Java või C#-ga).

1. Määratle tuumik (Rakendus)

Tuumikrakendus sisaldab äriloogikat uue kasutaja registreerimiseks.


// Core/UserService.java (või 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) {
        // Valideeri kasutaja sisend
        ValidationResult validationResult = userValidator.validate(username, password, email);
        if (!validationResult.isValid()) {
            return Result.failure(validationResult.getErrorMessage());
        }

        // Kontrolli, kas kasutaja on juba olemas
        if (userRepository.findByUsername(username).isPresent()) {
            return Result.failure("Username already exists");
        }

        // Räsita parool
        String hashedPassword = passwordHasher.hash(password);

        // Loo uus kasutaja
        User user = new User(username, hashedPassword, email);

        // Salvesta kasutaja hoidlasse
        userRepository.save(user);

        return Result.success(user);
    }
}

2. Määratle pordid

Määratleme pordid, mida tuumikrakendus kasutab välismaailmaga suhtlemiseks.


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

// Ports/PasswordHasher.java (või PasswordHasher.cs)
public interface PasswordHasher {
    String hash(String password);
}

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

//Ports/ValidationResult.java (või ValidationResult.cs)
public interface ValidationResult{
  boolean isValid();
  String getErrorMessage();
}

3. Määratle adapterid

Rakendame adapterid, mis ühendavad tuumikrakenduse konkreetsete tehnoloogiatega.


// Adapters/DatabaseUserRepository.java (või 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) {
        // Implementatsioon, kasutades JDBC, JPA või muud andmebaasi juurdepääsu tehnoloogiat
        // ...
        return Optional.empty(); // Kohatäide
    }

    @Override
    public void save(User user) {
        // Implementatsioon, kasutades JDBC, JPA või muud andmebaasi juurdepääsu tehnoloogiat
        // ...
    }
}

// Adapters/BCryptPasswordHasher.java (või BCryptPasswordHasher.cs)
public class BCryptPasswordHasher implements PasswordHasher {
    @Override
    public String hash(String password) {
        // Implementatsioon, kasutades BCrypt teeki
        // ...
        return "hashedPassword"; //Kohatäide
    }
}

//Adapters/SimpleUserValidator.java (või SimpleUserValidator.cs)
public class SimpleUserValidator implements UserValidator {
  @Override
  public ValidationResult validate(String username, String password, String email){
    //Lihtne valideerimisloogika
     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 (või 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 (või WebUserController.cs)
//Juhtiv adapter - käsitleb veebipäringuid
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();
        }
    }
}


4. Kompositsioon

Kõige kokkupanemine. Pange tähele, et see kompositsioon (sõltuvuste süstimine) toimub tavaliselt rakenduse sisenemispunktis või sõltuvuste süstimise konteineris.


//Põhiklass või sõltuvuste süstimise konfiguratsioon
public class Main {
    public static void main(String[] args) {
        // Loo adapterite instantsid
        DatabaseConnection databaseConnection = new DatabaseConnection("jdbc:mydb://localhost:5432/users", "user", "password");
        DatabaseUserRepository userRepository = new DatabaseUserRepository(databaseConnection);
        BCryptPasswordHasher passwordHasher = new BCryptPasswordHasher();
        SimpleUserValidator userValidator = new SimpleUserValidator();

        // Loo tuumikrakenduse instants, süstides adapterid
        UserService userService = new UserService(userRepository, passwordHasher, userValidator);

        //Loo juhtiv adapter ja ühenda see teenusega
        WebUserController userController = new WebUserController(userService);

        //Nüüd saate kasutaja registreerimistaotlusi käsitleda userControlleri kaudu
        String result = userController.registerUser("john.doe", "P@sswOrd123", "john.doe@example.com");
        System.out.println(result);
    }
}



//DatabaseConnection on lihtne klass ainult demonstreerimise eesmärgil
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;
    }

    // ... meetodid andmebaasiga ühendumiseks (lühendatud selguse huvides)
}

//Result klass (sarnane Eitherile funktsionaalses programmeerimises)
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;
    }

    // getterid ja setterid (lühendatud selguse huvides)

}

Selgitus:

Täpsemad kaalutlused ja parimad praktikad

Kuigi heksagonaalse arhitektuuri põhiprintsiibid on lihtsad, on mõned täpsemad kaalutlused, mida meeles pidada:

Reaalse maailma näited heksagonaalse arhitektuuri kasutamisest

Paljud edukad ettevõtted ja projektid on võtnud kasutusele heksagonaalse arhitektuuri, et ehitada vastupidavaid ja hooldatavaid süsteeme:

Väljakutsed ja kompromissid

Kuigi heksagonaalne arhitektuur pakub märkimisväärseid eeliseid, on oluline tunnistada sellega seotud väljakutseid ja kompromisse:

On ülioluline hoolikalt hinnata heksagonaalse arhitektuuri eeliseid ja väljakutseid teie konkreetse projekti nõuete ja meeskonna võimekuse kontekstis. See ei ole imerohi ja ei pruugi olla parim valik iga projekti jaoks.

Kokkuvõte

Heksagonaalne arhitektuur oma rõhuasetusega portidele ja adapteritele pakub võimsat lähenemist hooldatavate, testitavate ja paindlike rakenduste ehitamiseks. Eraldades tuumik-äriloogika välistest sõltuvustest, võimaldab see teil hõlpsasti kohaneda muutuvate tehnoloogiate ja nõuetega. Kuigi kaaluda tuleb väljakutseid ja kompromisse, kaaluvad heksagonaalse arhitektuuri eelised sageli üles kulud, eriti keerukate ja pikaealiste rakenduste puhul. Omaks võttes sõltuvuste ümberpööramise ja selgete liideste põhimõtteid, saate luua süsteeme, mis on vastupidavamad, lihtsamini mõistetavad ja paremini varustatud tänapäeva tarkvaramaastiku nõudmistele vastamiseks.

See juhend on andnud põhjaliku ülevaate heksagonaalsest arhitektuurist, alates selle põhiprintsiipidest kuni praktiliste rakendusstrateegiateni. Soovitame teil neid kontseptsioone edasi uurida ja katsetada nende rakendamist oma projektides. Investeering heksagonaalse arhitektuuri õppimisse ja kasutuselevõttu tasub end pikemas perspektiivis kahtlemata ära, viies kvaliteetsema tarkvara ja rahulolevamate arendusmeeskondadeni.

Lõppkokkuvõttes sõltub õige arhitektuuri valik teie projekti konkreetsetest vajadustest. Otsuse tegemisel arvestage keerukuse, pikaealisuse ja hooldatavuse nõuetega. Heksagonaalne arhitektuur pakub tugeva aluse vastupidavate ja kohandatavate rakenduste ehitamiseks, kuid see on vaid üks tööriist tarkvara arhitekti tööriistakastis.