Muster gegen anämisches Domänenmodell

2582
James

Ich möchte um Rat fragen, wie jeder das Anemic Domain Model-Anti-Pattern am besten bekämpfen kann , wenn ein auf Web-Services basierendes System entwickelt wird.

Eines unserer Ziele besteht darin, eine Reihe von Web-Kerndiensten aufzubauen, die die grundlegendsten Dienste offenlegen, die wir wiederholt in unserer Organisation wiederverwenden, dh das Erstellen von Domänenmodellen. Momentan haben wir eine kleine Bibliothek, die wir gemeinsam nutzen und wiederverwenden können, aber wenn wir unser Team vergrößern, wäre es viel angenehmer, diese Basisdienste zu zentralisieren. Im Laufe der Zeit werden sich unsere Systeme ändern, da einige Daten möglicherweise aus der Cloud stammen (Salesforce.com oder AWS). Daher isolieren wir nicht nur den grundlegenden DAO-Code in einem Webservice, sondern auch die Anwendungsintegration .

Unsere Kundendaten stammen beispielsweise aus den Bereichen Buchhaltung, CRM und Auftragsbearbeitung. Die Konfiguration ist sehr schwierig, da jede ausgelieferte App mit der Kernbibliothek und der Konfiguration auf jedem System gebündelt werden muss. Ich möchte die Erstellung von Modellen, z. B. SOA, zentralisieren, aber ein umfassendes Modell in der Service Layer / Facade beibehalten.

Wenn Sie im Allgemeinen denken, dass dies schlecht ist, würde ich gerne wissen, warum!

Mein Gedanke ist, ein Domänenobjekt zu definieren Employee, das ein EmployeeServiceinjiziert hat. Zur Laufzeit handelt es sich bei der EmployeeServiceImplementierung um eine EmployeeWebServiceClientImplImplementierung dieser Schnittstelle. EmployeeWebServiceClientImplverwendet einen Webdienst-Proxy für den Server.

Auf der Server-Seite des Web - Service haben wir den EmployeeWebServiceAufruf EmployeeDaodie Datenbank abzufragen. Könnte genauso einfach eine Klasse sein, die Salesforce.com auffordert, Daten abzurufen. Wir würden eine Bibliothek freigeben, die das Domänenmodell und die Schnittstelle enthielt, sodass Sie die Webdienstantwort direkt in eine Klasse deserialisieren könnten, die die erforderliche Geschäftslogik enthielt.

Nachfolgend finden Sie einige Beispielcodes in der Reihenfolge von Client zu Server:

//Example of client
public static void main(String[] args) {
    try {
        Employee employee = Employee.getEmployee("james");
        if (employee.isEligibleForRaise()) {
            System.out.println("Give that man a raise!");
        }
    } catch (RuntimeException e) {
        System.out.println("Oh no!");
    }
}


//Employee Domain Object
public class Employee {

  private String name;

  private String username;

  private static EmployeeService service;

  public static Employee getEmployee(String username) {
    return service.getEmployee(username);
  }

  public static List<Employee> getAllEmployees() {
    return service.getAllEmployees();
  }

  public boolean isEmployeeEligibleForRaise() {
    //business logic here
    return true;
  }


//Getters & Setters

 ...
}

//EmployeeWebServiceClientImpl
public class EmployeeWebServiceClientImpl implements EmployeeService {

  //A client web service proxy to our core basic services
  BaseWebServiceProxy proxy;

  @Override
  public Employee getEmployee(String username) {
    return proxy.getEmployee(username);
  }

  @Override
  public List<Employee> getAllEmployees() {
    return proxy.getAllEmployees();
  }
}


//On the server-side we have EmployeeWebService
public class EmployeeWebService implements EmployeeService {

  EmployeeDao employeeDao;

  @Override
  public List<Employee> getAllEmployees() {
    return employeeDao.getAllEmployees();
  }

  @Override
  public Employee getEmployee(String username) {
    return employeeDao.getEmployee(username);
  }
}

Ist das sinnvoll? Grundsätzlich besteht der Plan darin, die Kerngeschäftslogik im EmployeeDomänenobjekt beizubehalten und die Datenzugriffslogik in einem Webdienst zu isolieren.

Gedanken?

Antworten
11

3 Antworten auf die Frage

3
Visionary Software Solutions

Sie fügen hier viel Komplexität hinzu. Außerdem gehen Sie gegen den Punkt einer Service-Schicht und eines Domänenmodells. Ihr Domänenmodell sollte wahrscheinlich entweder Active Record oder Data Mapper verwenden .

Der Sinn einer Service Layer besteht darin, als Endpunkt (API) zu fungieren, der eine allgemeine Geschäftslogik enthält, die sich mit der Integration von Domänenmodellobjekten befasst. Ihre Service-Schicht sollte nur Repositorys abfragen und Aufrufe zur Verarbeitung der Geschäftslogik an Ihr Domänenmodell delegieren (das möglichst viel Geschäftslogik enthalten sollte). Das Injizieren in Ihr Domänenmodell fügt Persistenzprobleme hinzu, um die es sich nicht kümmern sollte.

Ich spreche nur über die beiden innersten Kreise von ServiceLayer: Domänenmodell und Datenquelle. Mein Beispiel oben ist trivial. In der Praxis wird mein Modell von SL / Facade aufgerufen. Ich hatte ActiveRecord im Sinn, aber es ist nicht so einfach wie das Beispiel auf Martin's Site. Wir sprechen selten direkt mit einer Datenbank. Unsere Daten stehen hinter Lieferanten-APIs (CRM, HR, Projektmanagement usw.), ActiveDirectory und DB. Um unser Modell zu erstellen, sprechen wir mit 6-7 verschiedenen Systemen. Die Motivation besteht darin, einen grundlegenden Satz von Diensten zu erstellen, der das Modell von einem zentralen Server (auch SOA) aus erstellt, jedoch noch ein reiches Modell in der Service Layer aufweist. James vor 9 Jahren 0
1
time4tea

Lassen Sie uns nicht Muster geschmackvoll erhalten.

Sie haben einen Angestellten, das ist gut.

Dann haben Sie eine Möglichkeit, sie aus verschiedenen Quellen zu finden? Nennen wir das mal Employees. Dann können Sie sie vielleicht bekommen und finden. Sie haben also Employees.findByName()- wo Sie davon ausgehen, dass es einen gibt, und einen Mitarbeiter zurückgibt oder wirft. Dann haben Sie möglicherweise eine Stelle, queryByName()an der Sie nicht wissen, ob es überhaupt eine gibt, die eine Liste zurückgibt oder die Liste durchläuft.

Dann haben Sie verschiedene Implementierungen. Ich persönlich denke, dass Impl eine schreckliche Sache ist. Es fügt weitere Buchstaben hinzu, ohne dass Sie mehr über die Implementierung erfahren. Wr entschied, dass es sich bei der Schnittstelle um Angestellte handelte. Jetzt haben wir vielleicht eine HttpEmployeesoder eine HibernateEmployees, sehen Sie, wir implementierten die Schnittstelle, gaben mehr Informationen über die Implementierung, mussten aber Impl nicht verwenden.

Sie haben einen kleinen Fehler gemacht, indem Sie das EmployeeService(was ich angerufen habe Employees) in die EmployeeKlasse setzen. Die Employeesollten nicht wissen, wie man Aufzeichnungen über den Angestellten führt.

Darüber hinaus sollten Sie sich bei statischen Methoden sehr vorsichtig verhalten. In diesem Fall würden Sie sagen, dass in einer bestimmten Anwendung Mitarbeiter nur aus einer Hand gefunden werden können, da Sie statisch sind. Warum instanziieren Sie nicht die Dinge, die die Employees-Schnittstelle mit einer bestimmten Implementierung verwenden müssen ...

Eine weitere Sache ... getAllEmployees()ist unwahrscheinlich nützlich. Viele Unternehmen haben Zehntausende oder Hunderttausende Angestellte .....

Ich stimme nicht mit der Vorstellung überein, dass statische Methoden für Finder schlecht sind und "Mitarbeiter sollten nicht wissen, ob sie Aufzeichnungen über Mitarbeiter führen". Mit diesen beiden Mustern können moderne ORM-Tools wie ActiveRecord (Ruby) und GORM (Groovy) das tun. Außerdem ist es ein klassisches Muster der aktiven EAA-Datensätze, wenn der Mitarbeiter jetzt selbst das Einfügen / Aktualisieren / Löschen selbst hat. Sehen Sie sich ein Beispiel für GORM an, um zu verstehen, was ich meine: http://grails.org/doc/1.0.x/guide/5.%20Object%20Relational%20Mapping%20(GORM).html James vor 9 Jahren 0
Die statische Methode führt eine enge Kopplung ein, die das Testen von Einheiten schwierig machen kann (Sie können zum Beispiel keinen scheinbaren Mitarbeiter verwenden). Ruby kann damit umgehen, weil es so dynamisch ist (ein Test kann die Employee-Klasse zur Laufzeit in MockEmployee ändern) Brad Cupit vor 9 Jahren 0
Wenn Sie nicht einen sehr schönen ORM verwenden (was bedeutet, wenn Sie Code haben, der direkt SQL verwendet, würde ich das in ein DAO einfügen und Ihr Objektmodell das DAO verwenden.) Brad Cupit vor 9 Jahren 0
Ich sehe zwei Verspottungsstrategien: 1) separate Angestelltenschnittstelle von der Implementierung, so dass Sie Angestellte verspotten können, und 2) Verspottung EmployeeService innerhalb der in statischen Suchern verwendeten Angestellten. Nie sind statische Finder-Methoden von der Verwendung behindert. James vor 9 Jahren 0
0
oopexpert

Sie haben einen harten Tag, um diesen Entwurf zu "bekämpfen". Meine Ansicht zu diesem Thema: Die Leute wollen entweder die Semantik umgehen, interessieren sich nicht dafür oder wissen gar nicht, dass so etwas existiert.

Da das "Anemic Domain Model" sich nicht für die Verkapselung interessiert, ist die Semantik leicht zu umgehen. Sie müssen nicht darüber nachdenken, wo sich Attribute und Algorithmen "am besten" befinden. Sie können die Strukturen, die Sie an einem bestimmten Ort benötigen, problemlos übergeben, um damit zu tun, was Sie möchten.

Ich muss "am besten" erklären, da dies der Hauptpunkt ist, den ich immer mit anderen Entwicklern zu kämpfen habe.

Ich schlage vor, auch den nächsten Absatz zu lesen, wenn Sie meinen Eindruck hier schlecht sehen. Zuallererst möchte ich erwähnen, dass es für mich nur eine einzige (die "beste") Lösung gibt. Der Punkt ist: Wenn Sie über die Codequalität sprechen und welche Art von Code besser ist, sagen alle implizit "Es gibt eine beste Lösung". Wenn Sie dies nicht tun: JEDE Anregungen zur "Verbesserung" von Code wären absolut subjektiv.

Auf der anderen Seite haben WE ALL irgendwann kognitive Schwierigkeiten und / oder die Programmiersprache hindert uns daran, die Semantik richtig auszudrücken. Deshalb bin ich überzeugt, dass wir fast nie den Punkt der "besten" Umsetzung erreichen. ABER ich bin auch überzeugt, dass wir den Weg durch Versuch und Irrtum und Fälschung bewerten können. In der Wissenschaft wird nichts anderes gemacht. Sie versuchen gute Realitätsmodelle zu erzeugen. Und sie wollen so nahe wie möglich sein, weil sie mit diesen Modellen Vorhersagen machen wollen, die für die Gesellschaft von Nutzen sind. Dafür müssen sie jedoch in der Semantik klar sein.

Eines möchte ich erwähnen, unter welcher Annahme meine Aussagen gemacht werden, wenn ich von "Realität" spreche:

Ich denke, wir teilen und erleben EINE Realität, mit der wir uns befassen müssen. Die andere Sache ist, dass wir alle unterschiedliche Wahrnehmungen der Realität haben. Unser Ziel sollte es sein, die besten Methoden zur Erklärung der Realität zu ermitteln.

Wir Entwickler versuchen auch, Realitätsmodelle zu erstellen. Es gibt jedoch Entwickler, die sich nicht für die Realität interessieren, da es Menschen gibt, die sich nicht für die Realität interessieren, die die Wissenschaft zu bewerten und zu beschreiben versucht. Der Punkt ist: Wir als Entwickler können im Programm in unseren Programmiersprachen erfinden, solange die Schnittstellen denjenigen, die uns eingestellt haben, positive Ergebnisse bringen. Wir können eine Sammlung unnötigerweise zweimal durchlaufen, das korrekte Ergebnis jedoch zurückgeben. Wir können riesige Strukturen aufbauen und sie sofort zerstören. Ich muss zugeben, dass meine Modelle im Gegensatz zu den von der Wissenschaft produzierten Modellen schlecht sind. Egal ob es die Schuld der Programmiersprache oder meiner kognitiven Probleme ist.

Also, wir als Entwickler bauen Modelle ... Das Ziel ist es, dass das Modell am besten mit der Realität übereinstimmt UND der Code, den wir produzieren, am besten zum Modell passt. Gegenwärtig wird objektorientiertes Paradigma als das beste Paradigma für das Modellieren von Elementen der realen Welt angesehen. Sie haben ein Auto in der Realität, Sie erstellen ein Modell eines Autos, schließlich erhalten Sie ein Objekt der Klasse "Auto" in Ihrer objektorientierten Sprache. In der Realität kann ein Auto gestartet werden, so dass Sie möglicherweise ein Modell zum Starten des Motors haben, das zu einer Methode "startEngine ()" führen kann.

Und hier kommt das Problem: Je spezifischer UND je abstrakter die Dinge sind, die wir modellieren müssen: Ich denke, wir bekommen kognitive Probleme. Dies korreliert leider auch mit der Wahrnehmung der Personen in der Realität.

Wir haben einen großen Vorteil gegenüber dem Management: Wir können nur von anderen Entwicklern verfolgt werden, die mit unserem Code vertraut sind. Dies wurde zu einem Zeitpunkt entdeckt, als Software auf den Markt kam, die nicht von einem Entwickler alleine entwickelt werden konnte. Zusammenarbeit war notwendig. Das Problem: Die unterschiedlichen Realitätswahrnehmungen der verschiedenen Entwickler (Personen).

Heutige Ansätze zur Lösung dieses Problems sind Erfahrung, IT-Know-how und hohe soziale Kompetenz.

Ich persönlich interessiere mich nicht für soziale Kompetenz, wenn ich hier Fragen beantworte. Also stelle ich das schärfste Schwert aus, mit dem man streiten muss, um es an Sie weiterzugeben: Die SOLID-Prinzipien zusammen mit dem Gesetz von Demeter.

Ich bin davon überzeugt, dass diese Prinzipien die Codequalität auf semantischer Ebene verbessern und Dinge aufzeigen, die die Realität beeinträchtigen, sodass Sie Ihr Modell besser an die Realität anpassen können.

Die SOLID-Prinzipien führen zu einem Domänenmodell, das eindeutig KEIN "anämisches Domänenmodell" ist.