Archiv der Kategorie: .NET Framework

Microservices don’t like thinking in conventional entities

I recently had one of these special AHA-moments in a workshop held by Udi Dahan, CEO of Particular. In his example he was using the classic entity of a customer.

To realise microservices means cutting functionality in a clean way and to transfer it into separate pillars (or silos). Each silo has to have the responsibility over it’s own data, on which it builds the respective business processes. So far, so good. But how can we realise this for our typical customer, who’s model is shown in the screenshot? Different properties are needed or changed by different microservices.

If the same entity is used in all pillars, there needs to be a respective synchronisation between all microservices. This results in a considerable impact on scalability and performance. In an application with lots of parallel changes of an entitiy the failing of the business processes will increase – or lead to inconsistencies in the worst case.

Conventional Customer

conventional entity of a customer

Udi suggests the following modelling

New Customer

Customer is modelled with independent entities

To identify which data belongs together, Udi suggests an interesting approach:

Ask the operating department if changing a property has a consequence for another property.

Would changing the last name of customer have an influence on the price calculation? Or on the way of marketing?

Now we need to solve the problem of aggregation, for example if I want to show different data from different microservices in my view. In a classic approach we would have a table with following columns:

ID_Customer ID_MasterData ID_Marketing ID_Pricing

This leads to the following two problems:

  1. The table needs to be extended if a new microservice is added
  2. If a microservice covers the same functionality in the form of data, you would have to add multiple columns for each microservice and allow NULL values as well

An example for the second point could be a microservice which covers the matter of payment methods. In the beginning you could only use credit cards and debit charges. Then Paypal. Bitcoin soon after. The microservice would have different tables on which the respective data for the payment method would be stored. In the aggregated table shown above it would be necessary to fill a separate column for each payment method the customer is using. If he doesn’t use it, a NULL value would be written. As you can see: This sucks.

Another approach is much more convenient for this. Which one this is and how it’s realised technically you can find on the GitHub repository of Particular.

 

Advertisements

Microservices mögen kein Denken in klassischen Entitäten

Einen ganz besonderen AHA Moment hatte ich kürzlich in einem Workshop bei Udi Dahan, CEO von Particular. In seinem Beispiel ging es um die klassische Entität eines Kunden.

Microservices zu realisieren bedeutet Fachlichkeiten sauber schneiden und in eigenständige Silos (oder Säulen) packen zu müssen. Jedes Silo muss dabei die Hohheit über die eigenen Daten besitzen, auf denen es die zugehörigen Geschäftsprozesse abbildet. Soweit so gut. Doch wie lässt sich dies im Falle eines Kunden bewerkstelligen, der klassischerweise wie im Screenshot zu sehen modelliert ist? Unterschiedliche Eigenschaften werden von unterschiedlichen Microservices benötigt bzw. verändert.

Wird die gleiche Entität in allen Silos verwendet, muss es eine entsprechende Synchronisierung zw. den Microservices geben. Das hat erhelbiche Auswirkungen auf Skalierbarkeit und Performance. In einer Applikation mit häufig parallelen Änderungen an einer Entität wird das Fehlschlagen von Geschäftsprozessen zunehmen – oder im schlimmsten Fall zu Inkonsistenzen führen.

Klassische Kundeentität

Klassische Kundeentität

 

Udi schlägt die folgende Modellierung vor:

Neue Modellierung eines Kunden

Der Kunde wird durch unabhängige Entitäten modelliert

Zur Identifikation, welche Daten zusammengehören, schlägt Udi einen Interessanten Ansatz vor:

Fragt die Fachabteilung, ob das Ändern einer Eigenschaft Auswirkung auf eine andere Eigenschaft hat. 

Würde das Ändern des Nachnamens einen Einfluss auf die Preiskalkulation haben? Oder auf die Art der Marketings?

Nun gilt es noch das Problem der Aggregation zu lösen, sprich wenn ich in meiner Anzeige unterschiedliche Daten unterschiedlicher Microserivces anzeigen möchte. Klassischerweise würde es jetzt eine Tabelle geben, die die Spalten

 

ID_Kunde ID_Kundenstamm ID_Bestandskundenmarketing ID_Preiskalkulation

 

besitzt. Das führt aber zu 2 Problemen:

  1. Die Tabelle muss immer erweitert werden, wenn ein neuer Microservices hinzugefügt wird.
  2. Sofern ein Microservices die gleiche Funktionalität in Form unterschiedlicher Daten abdeckt, müssten pro Microservices mehrere Spalten hinzugefügt und NULL Werte zugelassen werden.

Ein Beispiel für Punkt 2 wäre ein Microservices, der das Thema Bezahlmethoden abdeckt. Anfangs gab es beispielsweise nur Kreditkarte und Kontoeinzug. Dann folgte Paypal. Und kurze Zeit später dann Bitcoin. Der Microservices hätte hierzu mehrere Tabellen, wo er die individuelle Daten für die jeweilige Bezahlmethode halten würde. In oben gezeigter Aggregationstabelle müsste aber für jede Bezahlmethode, die der Kunde nutzt, eine Spalte gefüllt werden. Wenn er sie nicht benutzt, würde NULL geschrieben werden. Man merkt schon: Das stinkt.

Ein anderer Ansatz ist da deutlich besser geeignet. Welcher das ist und wie man diesen technischen realisieren kann, könnt ihr im GitHub Repository von Particular nachschauen.

 

Quellen zu Defensives Design und Separation of Concerns sind jetzt online

Den Quellcode zu meinen Vorträgen auf den Karlsruher Entwicklertagen und der DWX sind jetzt online:

  • Zu Super Mario Kata mit Fokus auf Defensivem Design geht es hier.
  • Zur Prüfsummen Kata mit fokus auf Separation of Concerns gelangt ihr hier.

Auf beiden Seiten findet ihr auch die Links zu den PowerPoint Folien. Im Juli 2018 werde ich den Code der Prüfsummen Kata noch in Form von Iterationen veröffentlichen und beide Talks als YouTube Video freigeben.

IMG_20180627_145405

Defensives Design Talk auf der DWX

Ihr wollt Teile davon in euren Vorträgen verwenden, ihr wollt ein Training zu dem Thema oder ich soll dazu in euerer Community einen Vortrag halten, dann kontaktiert mich über die auf GitHub genannten Kanäle.

Open Closed Principle mit 2 Zeilen Code

In meinem Video zu 60 Minuten mit der Pfadfinderregel war ich noch die Lösung zu Castle.Windsor schuldig. In diesem Webcast zeige ich wie die CollectionResolver-Funktionalität genutzt werden kann, um nachträglich hinzugefügte Implementierungen automatisch im Container zu registrieren.

Dadurch kann das OCP vollständig eingehalten werden, da lediglich eine neue Klasse hinzugefügt werden und der restliche Code nicht angefasst werden muss. Den Code habe ich hier zur Verfügung gestellt. Fragen und Feedback einfach in die Kommentare.

Durch Klicken auf das Bild geht es zum Video

Durch Klicken auf das Bild geht es zum Video

Die großen 4: Pfadfinderregel, Wirtschaftlichkeit, Clean Code, SOLID Principles

Was ist damit gemeint? Gemäß der Pfadfinderregel soll ein Entwickler Code immer besser hinterlassen, als er ihn vorgefunden hat. Clean Code oder guter Code ist häufig dann erreicht, wenn das Mindestmaß an essentiellen Code Prinzipien umgesetzt ist. Das sind die sogenannten SOLID Principles. Jedoch ist guter Code kein Selbstzweck, sondern dient dem größeren Ziel der Wirtschaftlichkeit.

Im folgenden Video zeige ich an einem Praxisbeispiel, wie ich bei einem bestehendem, eher unwichtigem Projekt vorgegangen bin. Timeboxed in 1h so viel refaktorisieren und den Code verbessern wie möglich. Dabei gehe ich auf Prinzipien wie DRY und OCP ein und zeige Techniken wie DI, sowie Tools wie den IoC Container Castle Windsor.

Feedback nehme ich wie immer gerne mit. Wenn ihr mehr von solchen Videos sehen wollt, schreibt mir das in die Kommentare, damit ich weiß: Hier lohnt es sich mehr zu machen.

Zum YouTube Video

Durch Klicken auf das Bild geht es zum Video

Build Server endlich ohne DevExpress

DevExpress ist leider alles andere als vorbildlich, was den Einsatz auf Build Servern angeht. Weder bietet das Unternehmen NuGet Packages, noch lässt sich die Lizenzprüfung auf dem Build Server deaktivieren.

Mit einem kleinen Trick lässt sich aber die Installation umgehen. Hierzu ruft ihr den Installer auf dem Build Server/Agent auf, loggt euch mit euren Daten an dem unten aufgeführten Screenshot ein und brecht nach erfolgreichem Login ab einfach ab.

DevExpress Login Dialog

Damit wurden mehrere Registry Einträge geschrieben, die leider maschinenspezifisch sind. Diese findet ihr hier:

  • HKLM\SOFTWARE\Classes\Licenses378852D-D597-4A32-B6D9-680A16A3CDA6\***
  • HKLM\SOFTWARE\Classes\Licenses\6F0F8269-1516-44C6-BD30-0E90BE27871C\***

Wenn ihr in den Projekten dann DLL-Referenzen aus einem Repository-Verzeichnis verwendet oder euch eigene NuGet Packages baut, benötigt ihr in Zukunft keine Installation mehr. Das gilt auch für Developer, die nicht an der UI mitentwickeln.

Wohin mit den Queries, Repositories und UnitOfWorks in meinen Anwendungsschichten

Am vergangenen .NET Open Space gab es eine Session namens „Repositories oder ORM“. Den Titel fand ich ein wenig unglücklich gewählt. Nutzt man nämlich einen OR-Mapper, so verwendet man implizit Repositories. Zumindest ist das beim Entity Framework der Fall. Dort entspricht der DbContext nämlich der UnitOfWork und die darauf implementierte generische Set-Methode liefert die Repositories zu allen Datenbank-Entitäten.

Grob gesagt sollte pro Geschäftsvorfall (aus Sicht der UI) ein DbContext erzeugt, darauf alle Änderungen und Abfragen ausgeführt und im Anschluss per Commit in einer Transaktion ausgeführt werden. Die Transaktion findet implizit statt, sodass ihr sicher sein könnte, dass entweder alles in die Datenbank geschrieben wurde oder nichts.

Nun wurde die Frage gestellt wie mit Abfragen zu Verfahren ist: Sollte man in allen Layern auf die Repositories zugreifen und dann per LINQ beliebig filtern? Ich bin der Meinung, dass man dies tunlichst unterlassen sollte. Stattdessen nutze ich in der Regel eine der zwei Möglichkeiten (welche sich beide gut zum Testen eigenen):

  • Eine dedizierte Repository-Implementierung, die intern die Abfragen kapselt. Das könnte zum Beispiel die Klasse ‚Rechnungen‘ mit der Methode ‚AlleMitDatumGrößerAls(DateTime datum)‘  sein. Idealerweise mappt das Repository ‚Rechnungen‘ dabei die Entitäten auf Business Objekte. AutoMapper kann in dem Fall viel Schreibarbeit abnehmen.
  • Sollten mehrere Repositories die gleiche Abfrage verwenden, so bietet es sich an die Query in eine eigene Klasse dafür zu extrahieren. Zum Beispiel die Klasse ‚FindeBenutzerMitId‘, welche im Konstruktor die Id übergeben bekommt. Auf der UnitOfWork lässt sich dann die Query z.B. in der Form ausführen: ‚uow.ExecuteQuery(new FindeBenutzerMitId(1))‘. Dafür muss allerdings erst die UnitOfWork um einen solchen Mechanismus erweitert werden. Ich hatte diesen Ansatz vor ein paar Jahren in diesem Video beschrieben.

Leider ist ein Blog Beitrag ungeeignet um ein solch komplexes Thema in der notwendigen Tiefe zu besprechen. Ziel war es daher nur Denkanstöße zu geben und eine Diskussion zu starten. Wer nützliche Ressourcen zu dem Thema hat oder wer andere Ansätze verfolgt, kann diese gerne in die Kommentare schreiben.

%d Bloggern gefällt das: