Einführung
Daher haben wir uns bereits für Umfang , Methodik und Architektur entschieden . Gehen wir von der Theorie zur Praxis und zum Schreiben von Code. Ich möchte mit Entwurfsmustern beginnen, die die Geschäftslogik beschreiben - Service und Interactor . Bevor wir uns jedoch mit ihnen befassen, werden wir die Strukturmuster - ValueObject und Entity - untersuchen . Wir werden uns in Rubinsprache entwickeln. In weiteren Artikeln werden wir alle für die Entwicklung erforderlichen Muster mithilfe der Variablenarchitektur analysieren. Alle Entwicklungen, die für diese Artikelserie gelten, werden in einem separaten Rahmen zusammengefasst.

Und wir haben bereits einen passenden Namen gefunden - LunaPark .
Aktuelle Entwicklungen auf Github .
Nachdem wir alle Vorlagen geprüft haben, werden wir einen vollwertigen Mikroservice zusammenstellen.
Also historisch
Eine komplexe Unternehmensanwendung, die in Ruby on Rails geschrieben wurde, musste überarbeitet werden. Es gab ein fertiges Team von Ruby-Entwicklern. Die Domain Driven Development-Methode war perfekt für diese Aufgaben, aber es gab keine schlüsselfertige Lösung in der verwendeten Sprache. Trotz der Tatsache, dass die Wahl der Sprache hauptsächlich von unserer Spezialisierung bestimmt wurde, erwies sie sich als recht erfolgreich. Unter allen Sprachen, die üblicherweise für Webanwendungen verwendet werden, ist Ruby meiner Meinung nach die ausdrucksstärkste. Und daher mehr als andere, die zur Modellierung realer Objekte geeignet sind. Dies ist nicht nur meine Meinung.
Das ist die Java-Welt. Dann haben Sie die Neuankömmlinge wie Ruby. Ruby hat eine sehr ausdrucksstarke Syntax, und auf dieser Grundstufe sollte es eine sehr gute Sprache für DDD sein (obwohl ich noch nicht von einer tatsächlichen Verwendung in solchen Anwendungen gehört habe). Rails hat viel Aufregung ausgelöst, da es die Erstellung von Web-Benutzeroberflächen endlich so einfach zu machen scheint, wie es die Benutzeroberflächen Anfang der neunziger Jahre vor dem Web waren. Im Moment wurde diese Funktion hauptsächlich zum Erstellen einiger der zahlreichen Webanwendungen verwendet, hinter denen nicht viel Domänenreichtum steckt, da selbst diese in der Vergangenheit äußerst schwierig waren. Ich hoffe jedoch, dass die Leute dies als Gelegenheit sehen, ihre Aufmerksamkeit mehr auf die Domäne zu richten, da der Teil der UI-Implementierung des Problems reduziert wird. Sollte die Verwendung von Ruby jemals in diese Richtung gehen, könnte dies meiner Meinung nach eine hervorragende Plattform für DDD darstellen. (Ein paar Infrastrukturteile müssten wahrscheinlich ausgefüllt werden.)
Eric Evans 2006
Leider hat sich in den letzten 13 Jahren nicht viel geändert. Im Internet finden Sie Versuche, Rails dafür anzupassen, aber alle sehen schrecklich aus. Das Rails-Framework ist schwer, langsam und nicht fest. Es ist sehr schwierig, ohne Tränen zu beobachten, wie jemand versucht, die Implementierung des Repository- Musters auf der Basis von ActiveRecord darzustellen. Wir haben uns entschlossen, ein Mikroframework zu übernehmen und es an unsere Bedürfnisse anzupassen. Wir haben Grape ausprobiert, die Idee mit der automatischen Dokumentation schien erfolgreich zu sein, aber ansonsten wurde sie aufgegeben und wir haben die Idee, sie zu verwenden, schnell aufgegeben. Und fast sofort begannen sie, eine andere Lösung zu verwenden - Sinatra . Wir verwenden es weiterhin für REST- Controller und Endpunkte .
RUHE?Wenn Sie Webanwendungen entwickelt haben, haben Sie bereits eine Vorstellung von der Technologie. Es hat seine Vor- und Nachteile, deren vollständige Auflistung den Rahmen dieses Artikels sprengt. Für uns als Entwickler von Unternehmensanwendungen ist der wichtigste Nachteil jedoch, dass REST (dies geht sogar aus dem Namen hervor) nicht den Prozess, sondern seinen Status widerspiegelt. Der Vorteil ist die Verständlichkeit: Die Technologie ist sowohl für Back-End-Entwickler als auch für Front-End-Entwickler klar.
Aber dann konzentrieren Sie sich vielleicht nicht auf REST, sondern implementieren Ihre http + json-Lösung? Selbst wenn Sie es schaffen, Ihre Service-API zu entwickeln, erhalten Sie viele Fragen, wenn Sie die Beschreibung an Dritte weitergeben. Viel mehr als wenn Sie den bekannten REST bereitstellen.
Wir werden die Verwendung von REST als Kompromisslösung in Betracht ziehen. Wir verwenden JSON aus Gründen der Übersichtlichkeit und des jsonapi- Standards, um Entwicklern keine Zeit mit heiligen Kriegen bezüglich des Anforderungsformats zu verschwenden.
Wenn wir in Zukunft Endpoint analysieren, werden wir feststellen , dass es ausreicht, nur eine Klasse neu zu schreiben, um die Ruhe loszuwerden. REST sollte sich also überhaupt nicht darum kümmern, wenn Zweifel bestehen.
Während des Schreibens mehrerer Microservices haben wir Grundlagen geschaffen - eine Reihe abstrakter Klassen. Jede solche Klasse kann in einer halben Stunde geschrieben werden. Der Code ist leicht zu verstehen, wenn Sie wissen, wofür dieser Code gedacht ist.
Hier traten die Hauptschwierigkeiten auf. Neue Mitarbeiter, die sich nicht mit DDD-Praktiken und einer sauberen Architektur befassten, konnten den Code und seinen Zweck nicht verstehen. Wenn ich diesen Code selbst zum ersten Mal sehen würde, bevor ich Evans lese, würde ich ihn als Vermächtnis betrachten, als Überentwicklung.
Um dieses Hindernis zu überwinden, wurde beschlossen, eine Dokumentation (Richtlinie) zu verfassen, die die Philosophie der verwendeten Ansätze beschreibt. Die Umrisse dieser Dokumentation schienen erfolgreich zu sein, und es wurde beschlossen, sie auf Habré zu setzen. Abstrakte Klassen, die von Projekt zu Projekt wiederholt wurden, wurde beschlossen, ein separates Juwel einzubauen.
Philosophie

Wenn Sie sich an einen klassischen Film über Kampfkunst erinnern, dann wird es einen coolen Typen geben, der sehr geschickt mit einer Stange umgeht. Ein Sechster ist im Wesentlichen ein Stock, ein sehr primitives Werkzeug, eines der ersten, das in menschliche Hände gefallen ist. Aber in den Händen des Meisters wird er zu einer beeindruckenden Waffe.
Sie können Zeit damit verbringen, eine Pistole zu erstellen, die Sie nicht ins Bein schießt, oder Sie können Zeit damit verbringen, die Schießtechnik zu erlernen. Wir haben 4 Grundprinzipien identifiziert:
- Sie müssen komplexe Dinge einfach machen.
- Wissen ist wichtiger als Technologie. Dokumentation ist für eine Person verständlicher als Code, man sollte sie nicht durch eine andere ersetzen.
- Pragmatismus ist wichtiger als Dogmatismus. Standards sollten den Weg weisen und keinen Begrenzungsrahmen festlegen.
- Strukturalität in der Architektur, Flexibilität bei der Auswahl der Lösungen.
Eine ähnliche Philosophie lässt sich beispielsweise im ArchLinux OS - The Arch Way nachvollziehen. Auf meinem Laptop hat Linux lange Zeit keine Wurzeln geschlagen, früher oder später ist es kaputt gegangen und ich musste es ständig neu installieren. Dies verursachte eine Reihe von Problemen, manchmal schwerwiegende Probleme wie die Unterbrechung der Arbeitsfrist. Aber nachdem ich 2-3 Tage mit der Installation von Arch verbracht hatte, fand ich heraus, wie mein Betriebssystem funktioniert. Danach begann sie ohne Fehler stabiler zu arbeiten. Meine Notizen halfen mir, es in ein paar Stunden auf neuen PCs zu installieren. Eine reichliche Dokumentation half mir, neue Probleme zu lösen.
Das Framework hat einen absolut hochrangigen Charakter. Die Klassen, die es beschreiben, sind für die Struktur der Anwendung verantwortlich. Lösungen von Drittanbietern werden verwendet, um mit Datenbanken zu interagieren, das http-Protokoll und andere Dinge auf niedriger Ebene zu implementieren. Wir möchten, dass der Programmierer den Code untersucht, wenn eine Frage auftaucht, und versteht, wie eine bestimmte Klasse funktioniert, und dass die Dokumentation es uns ermöglicht, zu verstehen, wie sie verwaltet werden. Wenn Sie das Motordesign verstehen, können Sie kein Auto fahren.
Framework
Es ist schwierig, LunaPark als Framework im üblichen Sinne zu bezeichnen. Rahmen - Rahmen, Arbeit - Arbeit. Wir drängen darauf, uns nicht auf den Umfang zu beschränken. Der einzige Frame, den wir deklarieren, ist derjenige, der der Klasse mitteilt, in der diese oder jene Logik beschrieben werden soll. Es handelt sich vielmehr um eine Reihe von Tools mit ausführlichen Anweisungen.
Jede Klasse ist abstrakt und hat drei Ebenen:
module LunaPark
Wenn Sie ein Formular implementieren möchten, das ein einzelnes Element erstellt, erben Sie von dieser Klasse:
module Forms class Create < LunaPark::Forms::Single
Wenn es mehrere Elemente gibt, verwenden wir eine andere Implementierung .
module Forms class Create < LunaPark::Forms::Multiple
Im Moment sind nicht alle Entwicklungen in perfekter Reihenfolge und Edelstein befindet sich im Alpha-Zustand. Wir werden es in Übereinstimmung mit der Veröffentlichung von Artikeln schrittweise zitieren. Das heißt, Wenn Sie einen Artikel über ValueObject
und Entity
, sind diese beiden Vorlagen bereits implementiert. Am Ende des Zyklus sind alle für den Einsatz im Projekt geeignet. Da das Framework selbst ohne einen Link zu sinatra \ roda wenig nützlich ist, wird ein separates Repository erstellt, das zeigt, wie Sie „alles vermasseln“ können, um Ihr Projekt schnell zu starten.
Das Framework ist in erster Linie eine Anwendung auf die Dokumentation. Nehmen Sie diese Artikel nicht als Dokumentation für das Framework wahr.
Kommen wir also zur Sache.
Wertobjekt
- Wie groß ist deine Freundin?
- 151
- Sie haben angefangen, sich mit der Freiheitsstatue zu treffen?
So etwas hätte in Indiana passieren können. Das menschliche Wachstum ist nicht nur eine Zahl, sondern auch eine Maßeinheit. Nicht immer können Attribute eines Objekts nur durch Grundelemente (Integer, String, Boolean usw.) beschrieben werden. Manchmal sind Kombinationen davon erforderlich:
- Geld ist nicht nur eine Zahl, es ist eine Zahl (Betrag) + Währung.
- Ein Datum besteht aus einem Tag, einem Monat und einem Jahr.
- Um das Gewicht zu messen, reicht uns eine einzige Zahl nicht aus, sondern es ist auch eine Maßeinheit erforderlich.
- Die Passnummer besteht aus einer Reihe und tatsächlich aus der Nummer.
Andererseits ist dies nicht immer eine Kombination, vielleicht ist es eine Art Erweiterung des Primitiven.
Eine Telefonnummer wird oft als Nummer genommen. Andererseits ist es unwahrscheinlich, dass er eine Methode der Addition oder Division haben sollte. Vielleicht gibt es eine Methode, die einen Ländercode ausgibt, und eine Methode, die einen Stadtcode definiert. Vielleicht gibt es eine bestimmte dekorative Methode, die es nicht nur als Zeichenfolge 79001231212
, sondern als lesbare Zeichenfolge darstellt: 7-900-123-12-12
.
vielleicht ein Dekorateur?Basierend auf Dogmen ist es unbestreitbar - ja. Wenn wir uns diesem Dilemma des gesunden Menschenverstandes nähern, werden wir, wenn wir uns entscheiden, diese Nummer anzurufen, das Objekt selbst auf das Telefon übertragen:
phone.call Values::PhoneNumber.new(79001231212)
Und wenn wir uns entschieden haben, es als String zu präsentieren, dann ist dies eindeutig für eine Person getan. Warum machen wir diese Zeile nicht sofort für eine Person lesbar?
Values::PhoneNumber.new(79001231212).to_s
Stellen Sie sich vor, wir erstellen die Online-Casino-Website Three Axes und verkaufen Kartenspiele. Wir brauchen die Klasse "Spielkarte".
module Values class PlayingCard < Lunapark::Values::Compound attr_reader :suit, :rank end end
Unsere Klasse hat also zwei schreibgeschützte Attribute:
- Anzug - Kartenanzug
- Rangkartenwürde
Diese Attribute werden nur beim Erstellen einer Karte festgelegt und können bei Verwendung nicht geändert werden. Natürlich können Sie eine Spielkarte nehmen und 8 streichen, Q schreiben, aber das ist nicht akzeptabel. In einer anständigen Gesellschaft werden Sie höchstwahrscheinlich erschossen. Die Unfähigkeit, Attribute nach dem Erstellen des Objekts zu ändern, bestimmt die erste Eigenschaft des Wertobjekts - die Unveränderlichkeit.
Die zweite wichtige Eigenschaft des Wertobjekts ist, wie wir sie vergleichen.
module Values RSpec.describe PlayingCard do let(:card) { described_class.new suit: :clubs, rank: 10 } let(:other) { described_class.new suit: :clubs, rank: 10 } it 'should be eql' do expect(card).to eq other end end end
Ein solcher Test schlägt fehl, da sie an der Adresse verglichen werden. Damit der Test erfolgreich ist, müssen wir Value-Obects nach Wert vergleichen. Dazu fügen wir eine Vergleichsmethode hinzu:
def ==(other) suit == other.suit && rank == other.rank end
Jetzt wird unser Test bestehen. Wir können auch Methoden hinzufügen, die für den Vergleich verantwortlich sind, aber wie vergleichen wir 10 und K? Wie Sie wahrscheinlich bereits vermutet haben, werden wir sie auch in Form von Wertobjekten präsentieren . Ok, jetzt müssen wir die zehn besten Clubs wie folgt initiieren:
ten = Values::Rank.new('10') clubs = Values::Suits.new(:clubs) ten_clubs = Values::PlayingCards.new(rank: ten, clubs: clubs)
Drei Zeilen reichen für Rubin. Um diese Einschränkung zu umgehen, führen wir die dritte Eigenschaft des Objektwerts ein - Umsatz. Lassen Sie uns eine spezielle Methode der .wrap
Klasse haben, die Werte verschiedener Typen annehmen und in die richtigen konvertieren kann.
class PlayingCard < Lunapark::Values::Compound def self.wrap(obj) case obj.is_a? self.class
Dieser Ansatz bietet einen großen Vorteil:
ten = Values::Rank.new('10') clubs = Values::Suits.new(:clubs) from_values = Values::PlayingCard.wrap rank: ten, suit: clubs from_hash = Values::PlayingCard.wrap rank: '10', suit: :clubs from_obj = Values::PlayingCard.wrap from_values from_str = Values::PlayingCard.wrap '10C'
Alle diese Karten sind einander gleich. Wenn die wrap
Methode zu einer guten Praxis wird, wird sie in eine separate Klasse eingeordnet. Aus Sicht des dogmatischen Ansatzes wird auch eine separate Klasse obligatorisch sein.
Hmm, was ist mit dem Platz im Deck? Wie kann man herausfinden, ob diese Karte ein Trumpf ist? Dies ist keine Spielkarte. Dies ist der Wert der Spielkarte. Dies ist genau die Inschrift 10, die Sie an der Ecke des Kartons führen.
Es ist notwendig, sich sowohl auf den Objektwert als auch auf das Primitiv zu beziehen, das aus irgendeinem Grund nicht in Ruby implementiert wurde. Von hier aus entsteht die letzte Eigenschaft - Object-Value ist an keine Domain gebunden.
Empfehlungen
Unter der Vielzahl von Methoden und Werkzeugen, die zu jedem Zeitpunkt eines jeden Prozesses verwendet werden, gibt es immer eine Methode und ein Werkzeug, die schneller und besser funktionieren als andere.
Frederick Taylor 1914
Arithmetische Operationen müssen ein neues Objekt zurückgeben
Attribute eines Wertobjekts können nur Grundelemente oder andere Wertobjekte sein
Behalten Sie einfache Operationen in Klassenmethoden bei
Wenn die "Konvertierungs" -Operation groß ist, ist es möglicherweise sinnvoll, sie in eine separate Klasse zu verschieben
Eine solche Entfernung der Logik in einen separaten Dienst ist nur unter der Bedingung möglich, dass der Dienst isoliert ist: Er verwendet keine Daten aus externen Quellen. Dieser Service sollte durch den Kontext des Wertobjekts selbst eingeschränkt sein.
Der Objektwert kann nichts über die Domänenlogik wissen
Angenommen, wir schreiben einen Online-Shop und haben eine Warenbewertung. Um es zu erhalten, müssen Sie über das Repository eine Anfrage an die Datenbank stellen.
Entität
Die Entity- Klasse ist für ein reales Objekt verantwortlich. Es kann ein Vertrag sein, ein Stuhl, ein Immobilienmakler, ein Kuchen, ein Bügeleisen, eine Katze, ein Kühlschrank - alles. Jedes Objekt, das Sie möglicherweise zur Modellierung Ihrer Geschäftsprozesse benötigen, ist eine Entität .
Das Konzept der Entität ist für Evans und für Martin unterschiedlich. Aus der Sicht von Evans ist eine Entität ein Objekt, das durch etwas gekennzeichnet ist, das seine Individualität betont.
Essenz von ZvansWenn ein Objekt durch eine eindeutige individuelle Existenz und nicht durch eine Reihe von Attributen bestimmt wird, sollte diese Eigenschaft beim Definieren eines Objekts in einem Modell als Haupteigenschaft gelesen werden. Die Definition der Klasse sollte einfach sein und auf der Kontinuität und Einzigartigkeit des Zyklus der Existenz des Objekts beruhen. Finden Sie einen Weg, um jedes Objekt unabhängig von seiner Form oder Existenzgeschichte zu unterscheiden. Achten Sie besonders auf die technischen Anforderungen, die mit dem Vergleichen von Objekten nach ihren Attributen verbunden sind. Definieren Sie eine Operation, die notwendigerweise ein eindeutiges Ergebnis für jedes dieser Objekte liefert. Möglicherweise muss dazu ein bestimmtes Symbol mit einer garantierten Eindeutigkeit verknüpft werden. Ein solches Identifikationsmittel kann einen externen Ursprung haben, es kann jedoch auch eine willkürliche Kennung sein, die vom System für seine eigene Bequemlichkeit erzeugt wird. Ein solches Werkzeug muss jedoch den Regeln zur Unterscheidung zwischen Objekten im Modell entsprechen. Das Modell sollte eine genaue Definition der identischen Objekte enthalten.
Aus Martins Sicht ist Entity kein Objekt, sondern eine Ebene. Diese Ebene kombiniert sowohl das Objekt als auch die Geschäftslogik zum Ändern.
Trostlosigkeit von MartinMeiner Ansicht nach enthalten Entitäten anwendungsunabhängige Geschäftsregeln. Sie sind nicht einfach Datenobjekte. Sie können Verweise auf Datenobjekte enthalten. Ihr Zweck ist es jedoch, Geschäftsregelmethoden zu implementieren, die von vielen verschiedenen Anwendungen verwendet werden können.
Gateways geben Entitäten zurück. Die Implementierung (unterhalb der Zeile) ruft die Daten aus der Datenbank ab und erstellt daraus Datenstrukturen, die dann an die Entitäten übergeben werden. Dies kann entweder mit Eindämmung oder Vererbung erfolgen.
Zum Beispiel:
öffentliche Klasse MyEntity {private MyDataStructure-Daten;}
oder
öffentliche Klasse MyEntity erweitert MyDataStructure {...}
Und denken Sie daran, wir sind alle von Natur aus Piraten. und die Regeln, über die ich hier spreche, sind wirklich eher Richtlinien ...
Mit Essenz meinen wir nur Struktur. In ihrer einfachsten Form sieht die Entity- Klasse folgendermaßen aus:
module Entities class MeatBag < LunaPark::Entities::Simple attr_accessor :id, :name, :hegiht, :weight, :birthday end end
Ein veränderliches Objekt, das die Struktur eines Geschäftsmodells beschreibt, kann primitive Typen und Werte enthalten .
Die LunaPark::Entites::Simple
Klasse ist unglaublich einfach, Sie können ihren Code sehen, sie gibt uns nur eines - einfache Initialisierung.
LunaPark :: Entites :: Simple module LunaPark module Entities class Simple def initialize(params) set_attributes params end private def set_attributes(hash) hash.each { |k, v| send(:"
Sie können schreiben:
john_doe = Entity::MeatBag.new( id: 42, name: 'John Doe', height: '180cm', weight: '80kg', birthday: '01-01-1970' )
Wie Sie wahrscheinlich bereits vermutet haben, möchten wir Gewicht, Größe und Geburtsdatum in Value Objects einbinden .
module Entities class MeatBag < LunaPark::Entites::Simple attr_accessor :id, :name attr_reader :heiht, :wight, :birthday def height=(height) @height = Values::Height.wrap(height) end def weight=(height) @height = Values::Weight.wrap(weight) end def birthday=(day) @birthday = Date.parse(day) end end end
Um keine Zeit mit solchen Konstruktoren zu verschwenden, haben wir eine komplexere Implementierung von LunaPark::Entites::Nested
vorbereitet:
module Entities class MeatBag < LunaPark::Entities::Nested attr :id attr :name attr :heiht, Values::Height, :wrap attr :weight, Values::Weight, :wrap attr :birthday, Values::Date, :parse end end
Wie der Name schon sagt, können Sie mit dieser Implementierung Baumstrukturen erstellen.
Lassen Sie uns meine Leidenschaft für sperrige Haushaltsgeräte befriedigen. In einem früheren Artikel haben wir eine Analogie zwischen der „Wendung“ einer Waschmaschine und der Architektur gezogen . Und jetzt beschreiben wir ein so wichtiges Geschäftsobjekt wie einen Kühlschrank:

class Refregerator < LunaPark::Entites::Nested attr :id, attr :brand attr :title namespace :fridge do namespace :door do attr :upper, Shelf, :wrap attr :lower, Shelf, :wrap end attr :upper, Shelf, :wrap attr :lower, Shelf, :wrap end namespace :main do namespace :door do attr :first, Shelf, :wrap attr :second, Shelf, :wrap attr :third, Shelf, :wrap end namespace :boxes do attr :left, Box, :wrap attr :right, Box, :wrap end attr :first, Shelf, :wrap attr :second, Shelf, :wrap attr :third, Shelf, :wrap attr :fourth, Shelf, :wrap end attr :last_open_at, comparable: false end
Dieser Ansatz erspart uns die Erstellung unnötiger Entitäten wie der Tür zum Kühlschrank. Ohne Kühlschrank sollte es Teil des Kühlschranks sein. Dieser Ansatz eignet sich zum Zusammenstellen relativ großer Dokumente, beispielsweise eines Antrags auf Abschluss einer Versicherung.
Die LunaPark::Entites::Nested
Klasse hat zwei weitere wichtige Eigenschaften:
Vergleichbarkeit:
module Entites class User < LunaPark::Entites::Nested attr :email attr :registred_at end end u1 = Entites::User.new(email: 'john.doe@mail.com', registred_at: Time.now) u2 = Entites::User.new(email: 'john.doe@mail.com', registred_at: Time.now) u1 == u2
Die beiden angegebenen Benutzer sind nicht gleichwertig, weil Sie wurden zu unterschiedlichen Zeiten erstellt und daher ist der Wert des Attributs registred_at
unterschiedlich. Aber wenn wir das Attribut aus der Liste der verglichenen streichen:
module Entites class User < LunaPark::Entites::Nested attr :email attr :registred_at, comparable: false end end
dann bekommen wir zwei vergleichbare Objekte.
Diese Implementierung hat auch die Eigenschaft des Umsatzes - wir können die Klassenmethode verwenden
Entites::User.wrap(email: 'john.doe@mail.com', registred_at: Time.now)
Sie können Hash, OpenStruct oder ein beliebiges Juwel als Entität verwenden , um die Struktur Ihrer Entität zu erkennen.
Eine Entität ist ein Modell eines Geschäftsobjekts. Lassen Sie es einfach. Wenn eine Immobilie von Ihrem Unternehmen nicht genutzt wird, beschreiben Sie sie nicht.
Entitätsänderungen
Wie Sie bemerkt haben, verfügt die Entity- Klasse über keine eigenen Änderungsmethoden. Alle Änderungen werden von außen vorgenommen. Das Wertobjekt ist ebenfalls unveränderlich. Alle Funktionen, die im Großen und Ganzen darin vorhanden sind, schmücken die Essenz oder schaffen neue Objekte. Die Essenz selbst bleibt unverändert. Für einen Ruby on Rails-Entwickler ist dieser Ansatz ungewöhnlich. Von außen mag es so aussehen, als würden wir die OOP-Sprache im Allgemeinen für etwas anderes verwenden. Aber wenn Sie etwas tiefer schauen, ist das nicht so. Kann sich ein Fenster von selbst öffnen? Ein Auto zur Arbeit bringen, ein Hotel buchen, eine süße Katze einen neuen Abonnenten bekommen? Dies sind alles äußere Einflüsse. In der realen Welt passiert etwas, und wir reflektieren dies in uns. Für jede Anfrage nehmen wir Änderungen an unserem Modell vor. Und damit halten wir es auf dem neuesten Stand, ausreichend für unsere Geschäftsaufgaben. Es ist notwendig, den Status des Modells und die Prozesse zu trennen, die Änderungen in diesem Status verursachen. Wie das geht, erfahren Sie im nächsten Artikel.