Benutzer-Werkzeuge

Webseiten-Werkzeuge


kunden_anlegen

Dies ist eine alte Version des Dokuments!


Vorschlag: Umstrukturierung der Käufererstellung nach 3-Schichten-Architektur Analyse der aktuellen Implementierung Aktuelle Struktur (Buyer::Create()) Die aktuelle Buyer::Create() Funktion enthält alle Schichten vermischt:

Validierung (ValidateData, ValidateVaucher, CheckDuplicate, CheckLager, CheckCountry) Geschäftslogik (setAgentByEmail, Generierung von Passwort/Kundennummer/Username, Bestimmung von frominternet) Datenbankzugriffe (InsertBuyer, SaveAdresses, SaveBank, direkte SQL-Queries) Cache-Operationen (CacheBuyers, CacheAddress) Externe Integrationen (E-Mail-Versand, CMP-Export, Letter Package) Orchestrierung (implizit durch die Reihenfolge der Aufrufe) Vorschlag: Neue Struktur Übersicht der Schichten API (CustomerController)

Orchestrierung (BuyerCreationOrchestration)

↓
├─→ Business-Funktionen (Fachfunktionen)
│   ├─→ ValidateBuyerDataFunction
│   ├─→ DetermineSponsorFunction
│   ├─→ GenerateBuyerIdentifiersFunction
│   ├─→ ValidateAddressesFunction
│   ├─→ DetermineRegistrationSourceFunction
│   └─→ ValidateVoucherFunction
↓

Zwischenschicht (Repositories)

├─→ BuyerRepository
├─→ AddressRepository
├─→ BankRepository
├─→ UserRepository
├─→ SponsorRepository
├─→ VoucherRepository
└─→ InterestingRepository
↓

Framework (Yii2) Detaillierte Aufteilung 1. API Layer Datei: backend/modules/api/controllers/CustomerController.php

Verantwortlichkeiten:

Empfang von HTTP-Requests Validierung des Request-Formats Aufruf der Orchestrierung Formatierung der Response Fehlerbehandlung auf API-Ebene Code-Struktur:

class CustomerController extends Controller {

  public function actionCreate($request)
  {
      try {
          // 1. Request-Validierung (Format, nicht Inhalt!)
          $this->validateRequestFormat($request);
          
          // 2. Aufruf Orchestrierung
          $orchestration = Yii::$container->get(BuyerCreationOrchestration::class);
          $result = $orchestration->createBuyer($request);
          
          // 3. Response formatieren
          return $this->formatResponse($result);
          
      } catch (ValidationException $e) {
          return $this->formatErrorResponse($e);
      } catch (\Exception $e) {
          return $this->formatErrorResponse($e);
      }
  }

} Was NICHT hier:

❌ Geschäftslogik ❌ Validierung von Geschäftsdaten ❌ Entscheidungen über Prozessablauf 2. Orchestrierung (BuyerCreationOrchestration) Datei: backend/modules/core/orchestration/BuyerCreationOrchestration.php

Verantwortlichkeiten:

Koordination des gesamten Erstellungsprozesses Aufruf der Business-Funktionen in richtiger Reihenfolge Datenbeschaffung über Zwischenschicht Zusammenführung der Ergebnisse KEINE Geschäftsentscheidungen Code-Struktur:

class BuyerCreationOrchestration {

  private $buyerRepository;
  private $validateBuyerDataFunction;
  private $determineSponsorFunction;
  private $generateIdentifiersFunction;
  private $validateAddressesFunction;
  private $determineRegistrationSourceFunction;
  private $validateVoucherFunction;
  private $saveBuyerFunction;
  private $saveAddressesFunction;
  private $saveBankFunction;
  private $sendEmailFunction;
  private $cacheService;
  private $cmpService;
  
  public function createBuyer($request)
  {
      // 1. Daten aus Request extrahieren
      $buyerData = $this->extractBuyerData($request);
      
      // 2. Validierung der Eingabedaten
      $validationResult = $this->validateBuyerDataFunction->validate($buyerData);
      if (!$validationResult->isValid()) {
          return $this->createErrorResponse($validationResult);
      }
      
      // 3. Bestimmung des Sponsors (agentid)
      $sponsorData = $this->determineSponsorFunction->determine($buyerData);
      $buyerData->merge($sponsorData);
      
      // 4. Validierung Gutschein (wenn aktiviert)
      if ($this->isVoucherValidationEnabled()) {
          $voucherResult = $this->validateVoucherFunction->validate($buyerData);
          $buyerData->merge($voucherResult);
      }
      
      // 5. Prüfung auf Duplikate (wenn nicht Guest)
      if (!$buyerData->isGuest() && !$buyerData->skipDuplicateCheck()) {
          $duplicateCheck = $this->checkDuplicates($buyerData);
          if ($duplicateCheck->hasDuplicates()) {
              return $this->createDuplicateResponse($duplicateCheck);
          }
      }
      
      // 6. Automatische Erstellung aus Interessent (wenn aktiviert)
      if ($this->isAutoCreateFromInterestingEnabled() && $buyerData->hasEmail()) {
          $interestingData = $this->buyerRepository->findInterestingByEmail($buyerData->getEmail());
          if ($interestingData) {
              return $this->createFromInteresting($interestingData, $buyerData);
          }
      }
      
      // 7. Bestimmung der Registrierungsquelle (frominternet)
      $registrationSource = $this->determineRegistrationSourceFunction->determine($buyerData);
      $buyerData->setRegistrationSource($registrationSource);
      
      // 8. Generierung von Identifikatoren
      $identifiers = $this->generateIdentifiersFunction->generate($buyerData);
      $buyerData->merge($identifiers);
      
      // 9. Validierung von Adressen
      if ($buyerData->hasAddresses()) {
          $addressValidation = $this->validateAddressesFunction->validate($buyerData->getAddresses());
          if (!$addressValidation->isValid()) {
              return $this->createErrorResponse($addressValidation);
          }
      }
      
      // 10. Validierung Lager (wenn angegeben)
      if ($buyerData->hasWarehouse()) {
          $warehouseValidation = $this->validateWarehouse($buyerData);
          if (!$warehouseValidation->isValid()) {
              return $this->createErrorResponse($warehouseValidation);
          }
      }
      
      // 11. Speicherung des Käufers
      $savedBuyer = $this->saveBuyerFunction->save($buyerData);
      
      // 12. Speicherung der Adressen
      if ($buyerData->hasAddresses()) {
          $this->saveAddressesFunction->save($savedBuyer->getId(), $buyerData->getAddresses());
      }
      
      // 13. Speicherung der Bankdaten
      if ($buyerData->hasBankData()) {
          $this->saveBankFunction->save($savedBuyer->getId(), $buyerData->getBankData());
      }
      
      // 14. Erstellung des Benutzers (WboUser)
      $user = $this->createUser($savedBuyer, $buyerData);
      
      // 15. Zusätzliche Aktionen
      $this->performPostCreationActions($savedBuyer, $buyerData);
      
      // 16. Cache aktualisieren
      $this->cacheService->cacheBuyer($savedBuyer->getId());
      $this->cacheService->cacheAddresses($savedBuyer->getId());
      
      // 17. Response zusammenstellen
      return $this->createSuccessResponse($savedBuyer, $user);
  }
  
  private function checkDuplicates($buyerData)
  {
      // Aufruf über Repository
      return $this->buyerRepository->checkDuplicates($buyerData);
  }
  
  private function validateWarehouse($buyerData)
  {
      // Business-Funktion für Lager-Validierung
      return $this->validateWarehouseFunction->validate($buyerData);
  }
  
  private function createUser($buyer, $buyerData)
  {
      // Orchestrierung für User-Erstellung (kann auch eigene Orchestrierung haben)
      $userOrchestration = Yii::$container->get(UserCreationOrchestration::class);
      return $userOrchestration->createUser($buyer, $buyerData);
  }
  
  private function performPostCreationActions($buyer, $buyerData)
  {
      // E-Mail-Versand (wenn nicht Guest)
      if (!$buyerData->isGuest()) {
          $this->sendEmailFunction->sendBuyerRegistrationEmail($buyer);
      }
      
      // Letter Package
      $this->letterPackageService->checkAndAdd($buyer, $buyerData);
      
      // Starter zu VP konvertieren (wenn starterid vorhanden)
      if ($buyerData->hasStarterId()) {
          $this->convertStarterToVP($buyerData->getStarterId());
      }
      
      // CMP-Export (wenn aktiviert)
      if ($this->isCmpExportEnabled()) {
          $this->cmpService->addToQueue($buyer->getId(), 1);
      }
  }

} Was NICHT hier:

❌ Geschäftslogik (z.B. „wenn agentid fehlt, dann…“) ❌ Direkte Datenbankzugriffe ❌ SQL-Queries ❌ Validierungsregeln 3. Business-Funktionen (Fachfunktionen) 3.1. ValidateBuyerDataFunction Datei: backend/modules/core/functions/ValidateBuyerDataFunction.php

Verantwortlichkeiten:

Validierung aller Käuferdaten nach Geschäftsregeln Prüfung von Pflichtfeldern Format-Validierung (E-Mail, IBAN, Steuernummer, etc.) Rückgabe von Validierungsergebnissen Code-Struktur:

class ValidateBuyerDataFunction {

  private $emailValidator;
  private $ibanValidator;
  private $taxIdValidator;
  private $usernameValidator;
  
  public function validate(BuyerData $buyerData): ValidationResult
  {
      $result = new ValidationResult();
      
      // 1. E-Mail-Validierung
      if ($buyerData->hasEmail()) {
          if (!$this->emailValidator->isValid($buyerData->getEmail())) {
              $result->addError('email', 'Invalid email format');
          }
      } else {
          $result->addError('email', 'Email is required');
      }
      
      // 2. Username-Validierung (wenn angegeben)
      if ($buyerData->hasUsername()) {
          if (!$this->usernameValidator->isUnique($buyerData->getUsername())) {
              $result->addError('username', 'Username already exists');
          }
      }
      
      // 3. Steuernummer-Validierung (wenn angegeben)
      if ($buyerData->hasTaxId()) {
          if (!$this->taxIdValidator->isValid($buyerData->getTaxId())) {
              $result->addError('taxId', 'Invalid tax ID');
          }
      }
      
      // 4. IBAN-Validierung (wenn Bankdaten vorhanden)
      if ($buyerData->hasBankData()) {
          $ibanValidation = $this->ibanValidator->validate($buyerData->getBankData());
          if (!$ibanValidation->isValid()) {
              $result->mergeErrors($ibanValidation);
          }
      }
      
      // 5. PLZ-Validierung (wenn Adressen vorhanden)
      if ($buyerData->hasAddresses()) {
          $plzValidation = $this->validatePostalCodes($buyerData->getAddresses());
          if (!$plzValidation->isValid()) {
              $result->mergeErrors($plzValidation);
          }
      }
      
      return $result;
  }
  
  private function validatePostalCodes(array $addresses): ValidationResult
  {
      $result = new ValidationResult();
      foreach ($addresses as $address) {
          if (!$this->postalCodeValidator->isValid($address->getPostCode(), $address->getCountry())) {
              $result->addError('postCode', 'Invalid postal code for country');
          }
      }
      return $result;
  }

} Was NICHT hier:

❌ Datenbankzugriffe (nur über Interfaces/Repositories) ❌ SQL-Queries ❌ Framework-spezifischer Code 3.2. DetermineSponsorFunction Datei: backend/modules/core/functions/DetermineSponsorFunction.php

Verantwortlichkeiten:

Bestimmung des Sponsors (agentid) nach Geschäftsregeln Automatische Bestimmung über E-Mail (wenn aktiviert) Rückgabe des Sponsor-Ergebnisses Code-Struktur:

class DetermineSponsorFunction {

  private $sponsorRepository;
  private $buyerRepository;
  private $config;
  
  public function determine(BuyerData $buyerData): SponsorResult
  {
      $result = new SponsorResult();
      
      // 1. Wenn agentid bereits vorhanden → validieren
      if ($buyerData->hasAgentId()) {
          $sponsor = $this->sponsorRepository->findByAgentId($buyerData->getAgentId());
          if (!$sponsor || !$sponsor->isActive()) {
              throw new InvalidSponsorException('Sponsor not found or inactive');
          }
          $result->setAgentId($sponsor->getAgentId());
          $result->setAgentInternalId($sponsor->getInternalId());
          return $result;
      }
      
      // 2. Automatische Bestimmung über E-Mail (wenn aktiviert)
      if ($this->config->isAgentByEmailEnabled() && $buyerData->hasEmail()) {
          $sponsor = $this->determineByEmail($buyerData->getEmail());
          if ($sponsor) {
              $result->setAgentId($sponsor->getAgentId());
              $result->setAgentInternalId($sponsor->getInternalId());
              return $result;
          }
      }
      
      // 3. Fallback auf Master-VP (Geschäftsregel)
      $masterVP = $this->sponsorRepository->findMasterVP();
      $result->setAgentId($masterVP->getAgentId());
      $result->setAgentInternalId($masterVP->getInternalId());
      
      return $result;
  }
  
  private function determineByEmail(string $email): ?Sponsor
  {
      // Geschäftsregel: Suche nach Käufer mit dieser E-Mail
      $buyer = $this->buyerRepository->findByEmail($email);
      if (!$buyer) {
          return null;
      }
      
      // Geschäftsregel: Bestimme Sponsor des gefundenen Käufers
      $sponsor = $this->sponsorRepository->findByBuyerId($buyer->getId());
      
      // Geschäftsregel: Sortierung (neueste/älteste)
      if ($this->config->getAgentByEmailMode() === 'newest') {
          // Neueste zuerst
          return $sponsor;
      } else {
          // Älteste zuerst
          return $sponsor;
      }
  }

} 3.3. GenerateBuyerIdentifiersFunction Datei: backend/modules/core/functions/GenerateBuyerIdentifiersFunction.php

Verantwortlichkeiten:

Generierung von Kundennummer (kundennr) Generierung von Username Generierung von Passwort (wenn nicht vorhanden) Bestimmung von accounting-Nummer Rückgabe aller generierten Identifikatoren Code-Struktur:

class GenerateBuyerIdentifiersFunction {

  private $buyerRepository;
  private $passwordGenerator;
  private $usernameGenerator;
  private $customerNumberGenerator;
  
  public function generate(BuyerData $buyerData): IdentifierResult
  {
      $result = new IdentifierResult();
      
      // 1. Kundennummer generieren
      $customerNumber = $this->generateCustomerNumber($buyerData);
      $result->setCustomerNumber($customerNumber);
      
      // 2. Username generieren
      $username = $this->generateUsername($buyerData, $customerNumber);
      $result->setUsername($username);
      
      // 3. Passwort generieren (wenn nicht vorhanden)
      if (!$buyerData->hasPassword()) {
          $password = $this->passwordGenerator->generate();
          $result->setPassword($password);
      } else {
          $result->setPassword($buyerData->getPassword());
      }
      
      // 4. Accounting-Nummer bestimmen
      $accounting = $this->determineAccountingNumber($buyerData, $customerNumber);
      $result->setAccounting($accounting);
      
      return $result;
  }
  
  private function generateCustomerNumber(BuyerData $buyerData): string
  {
      // Geschäftsregel 1: Wenn number angegeben → verwenden (nach Prüfung)
      if ($buyerData->hasNumber() && !$buyerData->hasImportNumber()) {
          // Prüfung auf Eindeutigkeit über Repository
          if ($this->buyerRepository->isCustomerNumberAvailable($buyerData->getNumber())) {
              return $buyerData->getNumber();
          }
          throw new CustomerNumberAlreadyExistsException();
      }
      
      // Geschäftsregel 2: Wenn mitarbeiternr vorhanden → verwenden
      if ($buyerData->hasMitarbeiternr()) {
          return $buyerData->getMitarbeiternr();
      }
      
      // Geschäftsregel 3: Automatische Generierung
      return $this->customerNumberGenerator->generate();
  }
  
  private function generateUsername(BuyerData $buyerData, string $customerNumber): string
  {
      // Geschäftsregel: Wenn username vorhanden → verwenden (bereits validiert)
      if ($buyerData->hasUsername()) {
          return $buyerData->getUsername();
      }
      
      // Geschäftsregel: Generierung basierend auf Kundennummer
      return $this->usernameGenerator->generateFromCustomerNumber($customerNumber);
  }
  
  private function determineAccountingNumber(BuyerData $buyerData, string $customerNumber): string
  {
      // Geschäftsregel 1: Wenn emplaccounting vorhanden → verwenden
      if ($buyerData->hasEmplAccounting()) {
          return $buyerData->getEmplAccounting();
      }
      
      // Geschäftsregel 2: Wenn accounting vorhanden → verwenden
      if ($buyerData->hasAccounting()) {
          return $buyerData->getAccounting();
      }
      
      // Geschäftsregel 3: Fallback auf Kundennummer
      return $customerNumber;
  }

} 3.4. ValidateAddressesFunction Datei: backend/modules/core/functions/ValidateAddressesFunction.php

Verantwortlichkeiten:

Validierung aller Adressen Prüfung auf Pflichtfelder Validierung von Ländern Prüfung auf Konsistenz Code-Struktur:

class ValidateAddressesFunction {

  private $countryRepository;
  private $postalCodeValidator;
  
  public function validate(array $addresses): ValidationResult
  {
      $result = new ValidationResult();
      
      foreach ($addresses as $index => $address) {
          // 1. Land-Validierung
          if (!$address->hasCountry()) {
              $result->addError("addresses[$index].country", 'Country is required');
              continue;
          }
          
          $country = $this->countryRepository->findByCode($address->getCountry());
          if (!$country) {
              $result->addError("addresses[$index].country", 'Invalid country code');
              continue;
          }
          
          // 2. PLZ-Validierung
          if ($address->hasPostCode()) {
              if (!$this->postalCodeValidator->isValid($address->getPostCode(), $country)) {
                  $result->addError("addresses[$index].postCode", 'Invalid postal code');
              }
          }
          
          // 3. Weitere Validierungen...
      }
      
      return $result;
  }

} 3.5. DetermineRegistrationSourceFunction Datei: backend/modules/core/functions/DetermineRegistrationSourceFunction.php

Verantwortlichkeiten:

Bestimmung von frominternet basierend auf autonumbertype Rückgabe der Registrierungsquelle Code-Struktur:

class DetermineRegistrationSourceFunction {

  public function determine(BuyerData $buyerData): RegistrationSource
  {
      // Geschäftsregel: Mapping von autonumbertype zu frominternet
      if ($buyerData->hasAutonumbertype()) {
          $type = $buyerData->getAutonumbertype();
          
          if ($type === 3) {
              return new RegistrationSource(107, 'Internet registration');
          }
          
          if ($type === 5) {
              return new RegistrationSource(111, 'Special registration');
          }
          
          // Standard für andere autonumbertype
          return new RegistrationSource(103, 'Internet registration with auto number');
      }
      
      // Standard-Registrierungsquelle
      return new RegistrationSource(102, 'Standard registration');
  }

} 3.6. ValidateVoucherFunction Datei: backend/modules/core/functions/ValidateVoucherFunction.php

Verantwortlichkeiten:

Validierung von Gutschein-Code Bestimmung des zugehörigen Sponsors Rückgabe des Sponsor-Ergebnisses Code-Struktur:

class ValidateVoucherFunction {

  private $voucherRepository;
  private $sponsorRepository;
  
  public function validate(BuyerData $buyerData): VoucherResult
  {
      $result = new VoucherResult();
      
      // Wenn kein agentid vorhanden → kein Gutschein-Check
      if (!$buyerData->hasAgentId()) {
          return $result;
      }
      
      // Prüfung ob agentid ein Gutschein-Code ist
      $voucher = $this->voucherRepository->findByCode($buyerData->getAgentId());
      if (!$voucher) {
          // Kein Gutschein → agentid bleibt unverändert
          return $result;
      }
      
      // Geschäftsregel: Sponsor aus Gutschein bestimmen
      $sponsor = $this->sponsorRepository->findByInternalId($voucher->getForAgent());
      if (!$sponsor) {
          throw new InvalidVoucherException('Sponsor for voucher not found');
      }
      
      // Ergebnis: agentid ersetzen, Gutschein-Code speichern
      $result->setAgentId($sponsor->getAgentId());
      $result->setAgentInternalId($sponsor->getInternalId());
      $result->setVoucherCode($buyerData->getAgentId());
      
      return $result;
  }

} 4. Zwischenschicht (Repositories) 4.1. BuyerRepository Datei: backend/modules/core/repositories/BuyerRepository.php

Verantwortlichkeiten:

Alle SQL-Queries für Käufer CRUD-Operationen Suche nach Käufern Duplikat-Prüfung Code-Struktur:

class BuyerRepository implements BuyerRepositoryInterface {

  private $db;
  
  public function create(BuyerEntity $buyer): BuyerEntity
  {
      // SQL-Query hier
      $sql = "INSERT INTO tb_kundendaten (...) VALUES (...)";
      $this->db->createCommand($sql, [...])->execute();
      
      // Stored Procedure aufrufen
      $this->db->createCommand("CALL kd_adresse(?)", [$buyer->getInternekdnr()])->execute();
      
      return $buyer;
  }
  
  public function findById(int $id): ?BuyerEntity
  {
      $sql = "SELECT * FROM tb_kundendaten WHERE internekdnr = ?";
      $row = $this->db->createCommand($sql, [$id])->queryOne();
      
      if (!$row) {
          return null;
      }
      
      return $this->mapToEntity($row);
  }
  
  public function findByEmail(string $email): ?BuyerEntity
  {
      $sql = "SELECT * FROM tb_kundendaten WHERE kdemail = ?";
      $row = $this->db->createCommand($sql, [$email])->queryOne();
      
      if (!$row) {
          return null;
      }
      
      return $this->mapToEntity($row);
  }
  
  public function isCustomerNumberAvailable(string $customerNumber): bool
  {
      $sql = "SELECT COUNT(*) FROM tb_kundendaten WHERE kundennr = ?";
      $count = $this->db->createCommand($sql, [$customerNumber])->queryScalar();
      return $count === 0;
  }
  
  public function checkDuplicates(BuyerData $buyerData): DuplicateCheckResult
  {
      // Komplexe SQL-Query für Duplikat-Prüfung
      $sql = "..."; // Aus ValidateDuplicate
      $rows = $this->db->createCommand($sql, [...])->queryAll();
      
      return $this->mapToDuplicateResult($rows);
  }
  
  public function generateNewCustomerNumber(): string
  {
      $sql = "SELECT getNewBuyerOutID() as newkdnr";
      return $this->db->createCommand($sql)->queryScalar();
  }
  
  private function mapToEntity(array $row): BuyerEntity
  {
      // Mapping von DB-Row zu Entity
      return BuyerEntity::fromArray($row);
  }

} 4.2. AddressRepository Datei: backend/modules/core/repositories/AddressRepository.php

Code-Struktur:

class AddressRepository implements AddressRepositoryInterface {

  private $db;
  
  public function create(int $buyerId, AddressEntity $address): AddressEntity
  {
      $sql = "INSERT INTO tb_kundendaten_zusatz (...) VALUES (...)";
      $this->db->createCommand($sql, [...])->execute();
      
      // Stored Procedure
      $this->db->createCommand("CALL kd_adresse_z(?)", [$address->getId()])->execute();
      
      return $address;
  }
  
  public function update(int $addressId, AddressEntity $address): void
  {
      $sql = "UPDATE tb_kundendaten_zusatz SET ... WHERE id = ?";
      $this->db->createCommand($sql, [...])->execute();
      
      $this->db->createCommand("CALL kd_adresse_z(?)", [$addressId])->execute();
  }
  
  public function findMainAddress(int $buyerId): ?AddressEntity
  {
      $sql = "SELECT * FROM tb_kundendaten_zusatz WHERE internekdnr = ? AND maindata = 1";
      $row = $this->db->createCommand($sql, [$buyerId])->queryOne();
      
      return $row ? $this->mapToEntity($row) : null;
  }
  
  public function findByReference(int $buyerId, string $reference): ?AddressEntity
  {
      $sql = "SELECT * FROM tb_kundendaten_zusatz WHERE internekdnr = ? AND reference = ?";
      $row = $this->db->createCommand($sql, [$buyerId, $reference])->queryOne();
      
      return $row ? $this->mapToEntity($row) : null;
  }
  
  public function generateNewAddressId(): int
  {
      // Generierung neuer ID
      $sql = "SELECT ...";
      return $this->db->createCommand($sql)->queryScalar();
  }

} 4.3. Weitere Repositories BankRepository - für Bankdaten UserRepository - für mlm_users und mlm_wbo_users SponsorRepository - für Partner/Sponsor-Daten VoucherRepository - für Gutscheine InterestingRepository - für Interessenten CountryRepository - für Länder WarehouseRepository - für Lager 5. Framework Layer Bleibt unverändert:

Yii2 Framework Datenbankverbindungen Sicherheit Routing Dependency Injection Container Mapping: Alte → Neue Struktur Aktuelle Funktion Neue Struktur Buyer::Create() BuyerCreationOrchestration::createBuyer() Buyer::ValidateData() ValidateBuyerDataFunction::validate() Buyer::ValidateVaucher() ValidateVoucherFunction::validate() Buyer::setAgentByEmail() DetermineSponsorFunction::determineByEmail() Buyer::CheckDuplicate() BuyerRepository::checkDuplicates() Buyer::getNewBuyerOutID() BuyerRepository::generateNewCustomerNumber() Buyer::CreateUserName() GenerateBuyerIdentifiersFunction::generateUsername() Buyer::InsertBuyer() BuyerRepository::create() Buyer::SaveAdresses() AddressRepository::create() / AddressRepository::update() Buyer::SaveBank() BankRepository::create() Buyer::SendBuyerMail() SendEmailFunction::sendBuyerRegistrationEmail() Cache::CacheBuyers() CacheService::cacheBuyer() Direkte SQL-Queries Alle in Repositories Vorteile der neuen Struktur Trennung der Verantwortlichkeiten:

Geschäftslogik ist klar getrennt von Datenzugriff Jede Funktion hat eine einzige Verantwortlichkeit Testbarkeit:

Business-Funktionen können ohne Datenbank getestet werden Repositories können isoliert getestet werden Orchestrierung kann mit Mocks getestet werden Wartbarkeit:

Änderungen an Geschäftsregeln → nur Business-Funktionen Änderungen an Datenbankstruktur → nur Repositories Framework-Wechsel → nur Repositories und API-Layer Erweiterbarkeit:

Neue Geschäftsregeln → neue Business-Funktionen Neue Datenquellen → neue Repository-Implementierungen Framework-Unabhängigkeit:

Core (Business-Funktionen, Orchestrierung) kann 1:1 auf Yii3 portiert werden Nur Repositories und API-Layer müssen angepasst werden Migrationsstrategie Phase 1: Vorbereitung Interfaces für alle Repositories definieren Entity-Klassen erstellen DTOs (Data Transfer Objects) für Request/Response Phase 2: Repositories extrahieren SQL-Queries aus Buyer::Create() in Repositories verschieben Buyer::Create() verwendet Repositories (noch alte Struktur) Phase 3: Business-Funktionen extrahieren Geschäftslogik in separate Funktionen auslagern Buyer::Create() wird zur Orchestrierung Phase 4: API-Layer anpassen Controller ruft Orchestrierung auf Alte Buyer::Create() wird deprecated Phase 5: Cleanup Alte Implementierung entfernen Tests schreiben Dokumentation aktualisieren Beispiel: Vollständiger Ablauf 1. API Request

 POST /api/v1/customers
 {
   "email": "test@example.com",
   "agentid": "10038",
   "adresses": [...]
 }

2. CustomerController::actionCreate()

  1. Request formatieren
  2. BuyerCreationOrchestration aufrufen

3. BuyerCreationOrchestration::createBuyer()

  1. ValidateBuyerDataFunction aufrufen
  2. DetermineSponsorFunction aufrufen
  3. GenerateBuyerIdentifiersFunction aufrufen
  4. BuyerRepository::create() aufrufen
  5. AddressRepository::create() aufrufen
  6. UserCreationOrchestration aufrufen
  7. CacheService aufrufen
  8. Response zusammenstellen

4. Business-Funktionen

  1. Führen Geschäftsregeln aus
  2. Rufen Repositories auf (nur lesend)
  3. Keine direkten DB-Zugriffe

5. Repositories

  1. Führen SQL-Queries aus
  2. Mappen Daten zu Entities
  3. Keine Geschäftslogik

6. Response

 {
   "status": "success",
   "data": {
     "internekdnr": 12345,
     "kundennr": "10038",
     "username": "user10038"
   }
 }

Zusammenfassung Die neue Struktur trennt klar:

API: Request/Response-Handling Orchestrierung: Koordination des Prozesses Business-Funktionen: Geschäftsregeln Repositories: Datenzugriff Framework: Technische Infrastruktur Alle aktuellen Funktionen bleiben erhalten, sind aber klar strukturiert und testbar.

kunden_anlegen.1769433845.txt.gz · Zuletzt geändert: von pldoku

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki