Home » Uncategorized » Zyklische Dependencies Vermeiden, mit Dependeny Inversion

Zyklische Dependencies Vermeiden, mit Dependeny Inversion

Contents

Problem

Wir haben ein Low Level (core) Module das von einem High Level (public Web) Module verwendet wird.
Nun gibt es einen neuen Fall, wo das High Module von vom Low Level Module aufgerufen werden muss. Dies ist eine umgekehrte Abhängigkeit!

edu.learn.highlevel.TuWasBean
interface HighLevelModuleTuWasServiceIntf{
  void tuWas(TuWasBean bean);
}

Das Problem ist, dass nun eine Zyklische Abhängigkeit (Cyclic Dependency) entsteht. Dies wird in unserem Fall zuallererst schmerzhaft, da High Level und Low Level Module im selben Build derselben Applikation gebaut werden. Dabei wird zuerst Low Level gebildet und dann High Level. Wenn man nun eine Anpassung am HighLevelModuleServiceIntf macht, dann muss man zuerst einen High Level Build laufen lassen, bevor man das Low Level auch adaptieren und bauen kann. (Sonst wäre ja das geänderte High Level Interface – auf das Low Level bereits aufbaut – noch gar nicht da.)

Lösung – Auflösung der Zyklischen Dependency

Die Lösung heisst „Dependency Inversion“. In unserem Fall heisst das, dass das Low Level Module so tut als sei der Service von ihm! Es definiert den Service selbst (und verlangt vom High Level Module, dass es selbst auf sein High Level Interface mappt).

Low Level Module formuliert also:

edu.learn.lowLevel.RepresentationOfTuWasBean
interface RepresentationOfTheHighLevelModuleTuWasServiceIntf{
   void tuWas(RepresentationOfTuWasBean bean);
}

Low Level codiert nun gegen sein eigenes Interface. Zur Runtime bezieht es vertrauensvoll eine Implementaion von RepresentationOfTheHighLevelModuleTuWasServiceIntf:

@Autowired
RepresentationOfTheHighLevelModuleTuWasServiceIntf service;

public void meinBusiness(){
   service.tuEtwas(RepresentationOfTuWasBean bean);
}

High Level muss dazu eine Implementierung des Low Level Interfaces zur Verfügung stellen. High Level Module mappt also selber das Low Level API zu seinem eigenen hin:

package edu.learn.highlevel.mapper;
import edu.learn.lowLevel.RepresentationOfTuWasBean
import edu.learn.highlevel.TuWasBean;

@Autowired
private OurBeanMapper beanMapper;

@Autowired
private HighLevelModuleTuWasServiceIntf highLevelService;

class TuWasServiceClientAdapter implements RepresentationOfTheHighLevelModuleTuWasServiceIntf {
   void tuWas(RepresentationOfTuWasBean lowLevelBean){
      TuWasBean highLevelBean = beanMapper.map(lowLevelBean);
      highLevelService.tuWas(highLevelBean);
   }
}

Durch diese Implementierung kann nun das High Level Module unabhängig vom Low Level Module Anpassungen an diesem API machen. Es ist einfach selbst verantwortlich das Low Level Interface weiterhin einzuhalten.
Das Low Level Module definiert den Zusammenarbeits-Vertrag. Das heisst: Auch hier ist nun High Level vom Low Level Abhängig.

Es ist nun auch möglich das Interface und dessen Nutzung (High und Low Level) anzupassen und gleichzeitig einem Build zuzuführen. Zuvor war das unmöglich, weil im ersten Schritt Low Level gebildet wurde, welches die angepassten Felder schon (zur Build-Zeit) im High Level Interface referenzierte. Nun kennt Low Level High level nicht mehr.

Zusätzlich Hilfreich …

… ist der Einsatz eines Bean-Mappers. Allein schon durch seinen Einsatz schlagen Feld-Anpassungen im Ziel-API nicht direkt auf Kompilierprobleme beim Consumer durch.


Hinterlasse einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert