Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Was ist falsch an meiner Schichtentrennung in einem ASP.NET MVC 5 Projekt?
MorphieX
myCSharp.de - Member



Dabei seit:
Beiträge: 184
Herkunft: Rahden

Themenstarter:

Was ist falsch an meiner Schichtentrennung in einem ASP.NET MVC 5 Projekt?

beantworten | zitieren | melden

Hallo,

ich fange gerade an meine erste größere Anwendung zu entwickeln und weiß jetzt schon, dass sie über Jahre wachsen und wahrscheinlich von unterschiedlichen Mitarbeitern gepflegt wird.
Daher lege ich großen Wert auf eine möglichst saubere Architektur von Anfang an.

Folgendes ist gegeben:

Infrastructure Assembly
- Interfaces für DI, z.B. IRepository, IUnitOfWork, IUserResolver,...
- Entitiy-Klassen und dazugehörige Enums

Data Access Assembly
- Entity Framework 6.2
- Auditing (also auch etwas Logik)
- Zugriff auf Infrastructure Assembly

Business Logic Assembly
- Implementierung der Repositories, UnitOfWork,...
- Utilities, z.B. Passwort-Verschlüsselungen
- Security
- Zugriff auf Infrastructure Assembly
- Zugriff auf Data Access Assembly

Presentation Layer
- Hauptprojekt
- ASP.NET MVC 5 (ja, kein ASP.NET MVC Core, das ist momentan Absicht, wird aber später sicherlich ausgetauscht
- In den Controllern werden meist CRUD-Operationen über die Repositories durchgeführt.
- jede View hat ein eigenes ViewModel, gemappt wird per AutoMapper
- Abhängigkeiten werden per DI aufgelöst
- Zugriff auf Business Logic Assembly
- Zugriff auf Infrastructure Assembly

Meine Projektstruktur sieht also zusammengefasst so aus:
[Presentation Layer] -> [Business Layer] -> [Data Access Layer]
[                      Infrastructure Layer                   ]


Nun zu meinen Fragen:
1) Gehört die Implementierung des Repositories in den BLL oder in die DAL? Ich habe halt Geschäftslogik in meinen Repositories.

2) Ich benutze im BLL die Entities, die mir das Entity Framework liefert. Das funktioniert so.
Ich lese aber in fast allen theoretischen Artikeln, dass da noch Business Objects dazwischen gehören. Also habe ich das mal ausprobiert und auch gleich wieder rückgängig gemacht, da ich gemerkt habe dass das 100% 1:1-Kopien meiner Entities waren. ;-) Irgendwas verstehe ich hier also noch nicht richtig.
Bisher habe ich auch noch kein Beispiel gefunden, bei dem das so gemacht wurde. Hat jemand einen Link zu einem entsprechenden Beispiel?
Sind eigene Business Objects bei Verwendung des Entity Frameworks vielleicht gar nicht notwendig?
Später kommen noch Objekte hinzu, die nicht (sofort) im Entity Framework landen - da kann ich mir das sehr gut vorstellen.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16112

beantworten | zitieren | melden

Repository im Sinne von Daten aus der Datenbank holen, gehören zur Datenschicht.
Diese machen nichts anderes als Entitäten zu schreiben und zu lesen.

In der Business Logik spricht man i.d.R. von Services; die dann aber trotzdem mit dem Repository Pattern umgesetzt werden können.
Zitat
2) Ich benutze im BLL die Entities, die mir das Entity Framework liefert. Das funktioniert so.
Funktioniert - ist aber aus Sicht der Software Architektur falsch.
Es ist akzeptabel für kleine Tools das so zu machen; für größere natürlich nicht.

Auch das EF ist nicht in der Lage ordentlich von Entity und Business Object (Domain Object) zu trennen.
Und in der Logik arbeitet man mit Domain Objects.

Ob Du das wirklich in Assemblies aufbauen musst; das ist nen Philosophie-Thema.
Ich mach das nur noch, wenn man muss.
Ansonsten trenne ich "Kern" der Anwendung von UI; sprich 2 Projekte.

Aber zu Deiner Technologieauswahl....

Wenn Du schon sagts, dass die Software Jahre bestehen wird, aber dann auf EF 6.2 und MVC 5 setzt. Das ist alles andere als "mal kurz" zu migrieren.
Vor allem EF würde ich mir nicht mehr ans Bein binden.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
MorphieX
myCSharp.de - Member



Dabei seit:
Beiträge: 184
Herkunft: Rahden

Themenstarter:

beantworten | zitieren | melden

Hallo Abt,

danke für deine Antwort :-)
Zitat
Und in der Logik arbeitet man mit Domain Objects.
So hatte ich es tatsächlich schon mal umgesetzt. Fühlte sich aber irgendwie nicht richtig an, weil viel Aufwand für (bislang) keinen zusätzlichen Nutzen. Aber ich werde es dann doch noch mal so versuchen.
Frage: Wenn es wirklich eine 1:1-Kopie ist, erbst du dann von der Entität?
Zitat
Ob Du das wirklich in Assemblies aufbauen musst; das ist nen Philosophie-Thema.
Im Moment hilft mir das, die Schichten nicht versehentlich zu durchbrechen. Ich denke wenn man da ein bisschen mehr Übung drin hat, kann man es sicher auch wagen die Schichten in einer gemeinsamen Assembly zu halten.
Zitat
Aber zu Deiner Technologieauswahl....
Hehe, mir war klar dass das sofort kommt ;-) Du hast ja auch absolut recht.
Im Moment "spiele" ich auch noch mit der Architektur und auch mit den Technologien. Genau diese beiden Schichten wollte ich aber absichtlich als nächsten "Meilenstein" austauschen.
Ich möchte ein Gefühl dafür bekommen, einzelne Programmteile in einer sauberen Architektur austauschen zu können. - wenn das ohne große Anpassungen in den anderen Programmteilen funktioniert, dürfte die Schichtentrennung doch erfolgreich gewesen sein.
Und wenn ich sage, dass das Projekt über mehrere Jahre gepflegt wird, dann gehe ich davon aus, dass irgendwann ein weiterer Technologiewechsel erfolgen wird.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von MorphieX am .
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16112

beantworten | zitieren | melden

Zitat von MorphieX
Fühlte sich aber irgendwie nicht richtig an

Es ist aber richtig.
Zitat von MorphieX
, weil viel Aufwand für (bislang) keinen zusätzlichen Nutzen.
Entitäten sind i.d.R. flacher geschnitten als Domain Objects.
Dann hattest Du womöglich einfach noch keine große Anwendung im Sinne einer großen Anwendung.

Bei kleinen Anwendungen ist es tatsächlich so, dass man es selten spürt.
Gefühlt hast Du jetzt auch alles andere als eine große Anwendung ;-)
Zitat von MorphieX
Wenn es wirklich eine 1:1-Kopie ist, erbst du dann von der Entität?
Niemals.

Ich hab mir dafür einfach nen eigenen, sehr simplen, sehr schnellen Mapper (https://github.com/SchwabenCode/FlexMapper) geschrieben.
Ich halte von Auto Mapper nicht viel, weil er es sehr komplex macht, sobald man mit Lazy-Informationen arbeiten (zB einem Datenbankkontext).
Zudem ist der AutoMapper halt (in Relation) langsam.
Zitat von MorphieX
Im Moment hilft mir das, die Schichten nicht versehentlich zu durchbrechen.
Genau. Dafür hilft es tatsächlich. Damit kann man die Faulheit von Entwicklern brechen ;-)

Zitat von MorphieX
Genau diese beiden Schichten wollte ich aber absichtlich als nächsten "Meilenstein" austauschen.
Das ist halt mehr als nur austauschen.
Ich will dafür sensibilisieren, dass das kein Versionupdate ist, sondern als ob Du eine komplette Technologie ersetzt.

Du musst auch von dem Gedanken etwas Abstand nehmen, dass Du was bauen wirst, in dem man alles einfach austauschen kann wie man lustig ist.
Das ist ein Wunschgedanken, der nie existieren wird.

Du wirst bei einem Technologiewechsel immer auch eine Migration durchlaufen - und das macht bei einem Grüne-Wiese-Projekt ehrlich gesagt wenig Sinn.
Und besonders beim EF zeigt die Historie, dass das alles andere als einfach ist - vor allem wenn man auch noch Migrations verwendet.

Das kostet richtig Zeit und Geld.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
Palin
myCSharp.de - Member



Dabei seit:
Beiträge: 1115

beantworten | zitieren | melden

Hi MarphieX,

zu. 1.) Die Repositories sollten eigentlich keine Geschäftslogik enthalten und gehören so mit eigentlich in den DAL.

zu. 2.) Nun man kann auch ohne Bussiness Objekte auskommen wenn man EF verwendet. Wenn die SQL DB in der 3. Normal Form ist. Ist da aber für ein Objekt keine schöne Struktur mehr. Und wenn du dann aus Performance gründen hin gehst und sie denormalisirst musst du dann auch deine ganz BL anfassen was man eigentlich nicht möchte.

Das zu den Fragen.
Deine Infrastruktur Assembly (bzw. es müssen nicht unbedingt Assembly sein Namespaces reichen aush) würde ich auf Teil (DAL -> IDAL, BL -> IBL usw.). Was du noch gebrachen kannst sind Cross Cutting Conceps. Da kannst du z.B. den Logger rein Packen.

Dann gibt es grundlegend noch eine harte und eine "weiche" Trennung bei der 3-Schichten Architektur.
Bei der "weichen" darf der PL auf den DAL zugreifen. Bei der harten Trennung laufen alle zugriffe über den BL. Wenn du hauptsächlich CRUD funktionen hast, ist der BL eigentlich unnötiger Ballast. Eine Trennung ist aber mit einer Generischen Basis Klasse recht einfach gemacht und falls später doch noch Logic hinzu kommt weist man wo man sie hin packen soll. Außer dem ist es später einfacher zu sagen, ich verzichte jetzt auf den BL und greife direkt auf den PL zu. Als den umgekehrten Schritt zu machen und nachher alle DAL zugriffe aus dem PL zu schmeißen.

Den Zugriff auf die Datenbank solltest du vielleicht noch über eine WebApi kapseln.
Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16112

beantworten | zitieren | melden

Zitat von Palin
zu. 2.) Nun man kann auch ohne Bussiness Objekte auskommen wenn man EF verwendet.

Sowas existiert nur in den Unicorn-Beispielen von Microsoft.
Das funktioniert in realen Anwendungen so gut wie nie, da Entitäten einfach vom Grundgedanke anders geschnitten werden als Business-Objekte / Domain Objects.

Hat auch nichts mit der Normalform einer DB zutun.
Die Logik kennt schließlich die DB überhaupt nicht.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
MorphieX
myCSharp.de - Member



Dabei seit:
Beiträge: 184
Herkunft: Rahden

Themenstarter:

beantworten | zitieren | melden

Danke :-D

Ich glaube ihr habt mir jetzt ein wenig die Augen geöffnet.

Könnt ihr mir vielleicht ein Praxis-Beispiel nennen, bei dem sich das Business-Objekt von der Entität unterscheidet? Theoretisch muss ein Business-Objekt doch auch über eine eindeutige ID und den Merkmalen der Entität verfügen.

Ich finde diese minimal-Beispiele von MS (und zig anderen) mittlerweile echt kontra-produktiv...

@Abt, bzgl. der Technologie-Auswahl werde ich damit loslegen, sobald ich das mit der Schichtentrennung sauber habe. Danke noch mal für den Hinweis. Ich habe das vielleicht doch als zu einfach empfunden.
private Nachricht | Beiträge des Benutzers
Palin
myCSharp.de - Member



Dabei seit:
Beiträge: 1115

beantworten | zitieren | melden

Zitat von Abt
Zitat von Palin
zu. 2.) Nun man kann auch ohne Bussiness Objekte auskommen wenn man EF verwendet.

Sowas existiert nur in den Unicorn-Beispielen von Microsoft.
Das funktioniert in realen Anwendungen so gut wie nie, da Entitäten einfach vom Grundgedanke anders geschnitten werden als Business-Objekte / Domain Objects.

Hat auch nichts mit der Normalform einer DB zutun.
Die Logik kennt schließlich die DB überhaupt nicht.

Eine Entity ist die Objekt Darstellung einer relationalen Datenbank. (OR Mapper). Das heißt indirekt sind dort auch Informationen über die Datenbank Struktur enthalten. Wenn ich die Entity jetzt im BL verwende kennt die BL indirekt die DB Struktur. Wenn ich die DB Struktur ändere, muss ich meist auch die Entity anpassen. Und damit dann den BL.
Mit der 3. Normal Form und wenn man diese bei einen OR Mapper verwendet, denke ist recht anschaulich dargestellt, das die Objekt Darstellung die entsteht, sich nicht wirklich für Bussines Objekte eignet.

Zitat von MarphieX
Könnt ihr mir vielleicht ein Praxis-Beispiel nennen, bei dem sich das Business-Objekt von der Entität unterscheidet? Theoretisch muss ein Business-Objekt doch auch über eine eindeutige ID und den Merkmalen der Entität verfügen.


Ist jetzt nicht unbedingt ein ein Business-Objekt, aber beim Anlegen von Daten hast man oft eine Auswahl liste (z.B. Anreder Herr/Frau usw). Die Liste ist nicht in der Entity enthalten, bei ihr gibt es eine eindeutige Zuordnung (von der Auswahl).

Ein Business-Objekt muss auch nicht zwangsläufig eine ID haben. Es gibt oft eine "natürliche" Identität. Z.B. wird aktuell meist oft die E-Mail Adresse verwendet. Die solltest du aber nicht in deiner DB als ID verwenden. Weil sie sich ändern kann.
Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16112

beantworten | zitieren | melden

Zitat von Palin
Wenn ich die DB Struktur ändere, muss ich meist auch die Entity anpassen. Und damit dann den BL.

Und das ist eine Verletzung und zeigt ein Fehldesign der Software Architektur auf.

Der BL muss nie angefasst werden, wenn das Mapping von Entität zu Business Object sauber abläuft.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
Palin
myCSharp.de - Member



Dabei seit:
Beiträge: 1115

beantworten | zitieren | melden

Zitat von Abt
Der BL muss nie angefasst werden, wenn das Mapping von Entität zu Business Object sauber abläuft.

Das ist auch genau der Grund wie so man keine Entitäten im BL verwenden sollte.
Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern
private Nachricht | Beiträge des Benutzers
MorphieX
myCSharp.de - Member



Dabei seit:
Beiträge: 184
Herkunft: Rahden

Themenstarter:

beantworten | zitieren | melden

Wo wird denn gemappt?
Wenn ich den BL wirklich als eigene Assembly führe, werden die Entities doch im BL in Business Objects gemappt. Und wenn ich nun eine Änderung an meine Entities vornehme, muss ich das Mapping ggf. anpassen, somit also den BL, oder?
Ich muss ja schließlich auch meine Business Objects anpassen, da die Änderung an den Entities (z.B. neues Datenbankfeld) auch im Business Object ankommen soll.

Oder findet das Mapping im DAL statt?

Beispiel:
Ich habe eine Entität "PersonEntity"
- ID
- Nachname
- Vorname

Ich habe dazu das BusinessObject "Person"
- ID
- Nachname
- Vorname
- VollstaendigerName

Nun erweitere ich meine Entity:
- ID
- Nachname
- Vorname
- Anrede

also muss ich auch das BusinessObject anpassen:
- ID
- Nachname
- Vorname
- Anrede <- neu
- VollstaendigerName <- das Mapping muss ebenfalls angepasst werden

Gut, man könnte jetzt sagen dass das Mapping nicht im BL stattfindet, weil es woanders konfiguriert wird (z.B. AutoMapper-Config) und dass die BusinessObjects nicht unbedingt im BusinessLayer definiert sein müssen, sondern z.B. in der Infrastructure, aber das eigentlich gehört das für mich zum BL
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16112

beantworten | zitieren | melden

Zitat von MorphieX
Wo wird denn gemappt?

Das ergibt sich ja allein anhand der Schichten.
Da der DAL nicht die BL kennen darf, muss der Mapper Teil des BL-Projekts sein.

Bevor also Daten den Service erreichen, muss gemappt werden.

zB.

ArticleEntity e = _articleRepository.Get();
Article a = ArticleMapper.FromEntity(ArticleEntity);

Damit bleibt der Business Layer völlig frei vom Inhalt einer Entität (sondern hat nur die Referenz auf die Abfrage).
Die Entität kann sich damit so viel ändern wie sie will; das Business Objekt bleibt unberührt und nur der Mapper muss angepasst werden.

Und natürlich nicht: Business Objekte gehören in den Business Layer und nicht in Infrastructure.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
Palin
myCSharp.de - Member



Dabei seit:
Beiträge: 1115

beantworten | zitieren | melden

Die Frage ist ob du da nicht eher DTO bzw. Models hast.

Wir haben bei uns eine eigene Assambly für die Models. Das kennt sowohl PL, BL und DAL (nicht alle Models werden von den Einzelnen Layern Verwendet).

Wir Mappen bei uns im DAL. Das hat bei uns den Grund, das Maschinen und ein altes VB6 unterstützt werden müssen. Die Teils noch auf Ini Files und Access Datenbanken zugreifen. Und beim Ini File habe ich keine Entities. Da sollte der BL sie auch nicht kennen.

Wir haben bis ca. vor einem Jahr noch ASP MVC verwendet. Da haben wir die Models auch direkt in der View verwendet. Dann sind wir nach Aurelia umgestiegen. Aus dem Models konnten, dann Type Script Klassen generieren und die Controller brauchten wir dann nur so abzuändern, das sie nur das Model und nicht mehr die View zurückgegeben haben. Hat ganz gut geklappt.
Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16112

beantworten | zitieren | melden

Zitat von Palin

Wir Mappen bei uns im DAL.

Das heisst der DAL hat eine Referenz auf den BL und der BL auf den DAL?
Das wäre ne Circular Dependency.
Zitat von Palin
Wir haben bis ca. vor einem Jahr noch ASP MVC verwendet. Da haben wir die Models auch direkt in der View verwendet.

Klassische Schichtenverletzung.

Wenn Du ein User hast mit einem Passwort-Property, würdest Du dieses damit exposen.
Sicherlich nicht Sinn der Sache.
Zitat von Palin
Hat ganz gut geklappt.
Auch die Microsoft Beispiele klappen immer gut, weil Unicorn.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
Palin
myCSharp.de - Member



Dabei seit:
Beiträge: 1115

beantworten | zitieren | melden

Zitat von Abt
Zitat von Palin

Wir Mappen bei uns im DAL.

Das heisst der DAL hat eine Referenz auf den BL und der BL auf den DAL?
Das wäre ne Circular Dependency.
Zitat von Palin
Wir haben bei uns eine eigene Assambly für die Models. Das kennt sowohl PL, BL und DAL (nicht alle Models werden von den Einzelnen Layern Verwendet).

PL, BL und DAL kennen also das Modell. Das Model selber kennt niemanden.
Zitat von Abt
Zitat von Palin
Wir haben bis ca. vor einem Jahr noch ASP MVC verwendet. Da haben wir die Models auch direkt in der View verwendet.

Klassische Schichtenverletzung.

Also soweit ich weiß steht das M in MVC für Modell. Und im MVC Pattern kann die View das Modell kennen. Er kläre hier mir bitte mal Fachlich, wo eine Schichtverletzung vorliegt.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Palin am .
Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16112

beantworten | zitieren | melden

Zitat
PL, BL und DAL kennen also das Modell. Das Model selber kennt niemanden.
Und das sollte man ja eigentlich nicht so machen, denn jede Präsentationsschicht hat i.d.R. seine eigenen Modelle.
Daran ändert auch das M in den Pattern-Bezeichnungen nichts.

Du kannst kein Modell in ASP.NET HTML, ASP.NET API, WinForms und einer App teilen - bzw. funktioniert das bei nur sehr sehr sehr einfachen Modellen - und/oder wenn man die Funktionalitäten der einzelnen UI-Technologien vollkommen ignoriert.

Das Modell, das eine API zurück gibt (eine API ist im Prinzip auch nichts anderes als die UI aus Sicht der API-Anwendung) gibt ganz andere Inhalte und Strukturen (Stichwort Hypermedia) aus als zB. WPF (Stichwort Observables).
Dabei kann/wird aber die Business Logik in beiden Fällen vollkommen identisch sein. Würde mich jetzt ehrlich gesagt wundern, wenn ihr hier was neues erfunden habt, das dieses Technologie-Grundproblem lösen würde.

Daher gehört das entsprechende View-Modell auch nicht in die BL.
Zudem findet das Mapping auf UI-Seite statt.

Zitat von Palin
Zitat von Abt
Wenn Du ein User hast mit einem Passwort-Property, würdest Du dieses damit exposen.
Sicherlich nicht Sinn der Sache.

Nette Unterstellung. Kann ich auch. Das ist nur wirklich ein Problem wenn man wie du die Passwörter im Klartext in der DB speichert.
Aber selbst die "Hash Werte" Reiche ich nicht bis zur UI durch.

Es ist sehr schade, wie Du (leider wieder) ein einfaches, sachliches Fallbeispiel (das ich in dieser Form in diesem Forum schon zig Male verwendet habe, weil es alltäglich ist) als persönlichen Angriff wertest und daraus (leider wieder) einen persönlichen Angriff startest.
Was willst Du damit erreichen?
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
MorphieX
myCSharp.de - Member



Dabei seit:
Beiträge: 184
Herkunft: Rahden

Themenstarter:

beantworten | zitieren | melden

Was haltet ihr davon, wenn wir (die Community) ein Beispielprojekt erarbeiten, auf welches man dann im [Artikel] Drei-Schichten-Architektur bzw. unter die regelmäßig gestellten Fragen zu diesem Thema verlinken könnte?

So simpel wie möglich und so komplex wie nötig.

Eventuell auch in unterschiedlichen Variationen, z.B. ohne Tools wie AutoMapper oder EF (damit man wirklich sieht was da eigentlich passiert) und mit Tools, damit man sieht was man an Arbeit spart ;-)

Ich habe zumindest überall nur Beispiele gefunden, die entweder falsch; viel zu komplex oder viel zu simpel gehalten sind.

Das wäre aus meiner Sicht ein echter Mehrwert.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16112

beantworten | zitieren | melden

Das wären aber 10< Beispiele, die natürlich auch gepflegt gehören - und trotzdem nicht alles abdecken werde.
Entwickler ist man ja auch, weil man entwickelt und nachdenken sollte. Für alles kann es keine Templates zum Kopieren geben.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
Palin
myCSharp.de - Member



Dabei seit:
Beiträge: 1115

beantworten | zitieren | melden

@Abt
Es gibt doch einige Anwendungen, da geht es Zentral darum das der Benutzer Daten eingibt und Speichert. Die Verwenden Zentral die CRUD Funktionen. Da geht es Zentral darum die Daten von der View in die Datenbank zu bekommen. Was dazu fuhrt das mein Model Gleich dem Bussines Object ist. Und das ist ähnlich dem Entety Model. Wenn ich jetzt eine Änderung im Entety Model mach. Muss ich auch, das Model und das Bussiness Objekt ändern. Und die UI Anpassen, soll ja auch angezeigt werden. Und ich brauche noch 2 Mapper, die ich gegebenen Falls auch anfassen muss. Dann brauch der PL noch einen Verweis, auf die Bussiness Objekte des BL (also einen Verweis auf die Implementierung BL)under BL auf die Entitäten (also einen Verweis auf die Implementierung DAL). Und um dann die Entitäten Richtig Mappen zu können muss der BL auch deren Abhängigkeiten kennen.
Dazu kommt dann noch das nicht alle Daten Zugriffs Technologien Entitäten unterstützen.

Wenn ich den DAL die Daten auf ein Objekt Mappen lasse, die (Objekte) kann ich in ein eigens Assembly packen. Braucht der BL nur ein die Interface des DALs zu kennen. Welches ich auch in ein eigenes Assembly Packen kann. Und mein BL kann jedes Assembly verwenden, welche die Schnittstellen des DAL implementiert. Bei Änderungen muss ich dann die Klasse mit dem Objekten ändern. Den DAL und da das Mapping anpassen. Und wenn ich die Daten bis in die UI so verwenden kann, brauche ich nur die UI Anpassen. Und falls ich doch ein Komplizierteres Bussines Model Brauche, kann ich es mir immer noch erstellen und Mappen.

@MorphieX
Bei der Disskusion zu Drei Schichten Architektur ist ein wirklich einfaches Beispiel zu finden.
Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16112

beantworten | zitieren | melden

Das hört sich ehrlich gesagt nicht so an, als ob es für mehrere Oberflächen/Technologien ausgelegt ist - Du hast hier ja laut Beschreibung eine (unnötige?) Bindung und schränkst Dich damit ein.
Das würde (offensichtlich?) knallen, wenn zB. ASP.NET auf WPF trifft.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
Palin
myCSharp.de - Member



Dabei seit:
Beiträge: 1115

beantworten | zitieren | melden

Die Abhängigkeit zu den Entitäten, habe im Prinzip gegen die zur "Modell" Klasse getauscht. Dafür kann (muss aber nicht) ich auf eine Klasse (die Redundant ist) und einen Mapper verzichten (PL<->BL).

Wenn wir uns mal das ASP MVC Modell aus dem Beispiel anschauen. Das kann ich bei MVC Verwenden. Das Objekt lässt sich aber auch Problem Los als Json Serialisieren. So das ich per WebApi bereitstellen kann. Eine Type Skript Klasse kann ich auch daraus erstellen lassen. Das Model kann ich auch als Model bei WPF im MVVM Pattern benutzen. Falls ich das PropertyChange Brauche kann ich es als nicht serilisierbar kennzeichnen (Ob es jetzt mit TypeScript funktioniert weiß ich nicht aber ich denke da gibt es eine Möglichkeit). Und bei Windoes Forms kann ich es auch nutzen. Bei Bedarf kann ich es immer noch auf eine Andere Struktur Mappen.

Das ist natürlich jetzt ein Einfaches Beispiel, es geht auch Komplexer. Nur sicher nicht Beliebig Komplex.
Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16112

beantworten | zitieren | melden

Ich nenne es mal Zufall, dass es bei Dir funktioniert.
Meine Erfahrung sagt, dass es eigentlich kein realen Fall gibt, der nicht ohne Workaround damit auskommt.

Daher finde ich die Beispiele dazu auch von Microsoft einfach nicht gut und realitätsnah.
Teilweise sogar eklatant falsch (zB. EF Beispiel mit Http Client)
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
Palin
myCSharp.de - Member



Dabei seit:
Beiträge: 1115

beantworten | zitieren | melden

Da fällt mir die Signatur von jemanden hier im Forum ein.
(Frei zitiert)
„Alles „wussten“, das es nicht Funktioniert. Da kam jemand der wusste das nicht und tat es einfach.“

Kann natürlich sein, das es bei mir Zufall ist. Und Erfahrung ist natürlich gut. Aber wissen ist da besser.

p.s.
Bevor mich da jemand Falls Versteht wir konnten unser „Daten“ Model Problemlos übernehmen, bei der Umstellung von MVC nach Aurelia. Bei Komplexeren Sachen, die manchmal echt schwer waren mit MVC umzusetzen. Wurde schon einiges (im Controller) geändert, was dann mir Aurelia deutlich leichter war.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Palin am .
Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16112

beantworten | zitieren | melden

Zitat
Und Erfahrung ist natürlich gut. Aber wissen ist da besser.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
MorphieX
myCSharp.de - Member



Dabei seit:
Beiträge: 184
Herkunft: Rahden

Themenstarter:

beantworten | zitieren | melden

Zitat von Palin
@MorphieX
Bei der Disskusion zu Drei Schichten Architektur ist ein wirklich einfaches Beispiel zu finden.
Meinst du "ProductCatalogue"? Ich weiß ja nicht...
Im Prinzip ist in dem Beispiel alles vom DataSet abhängig, und damit meine ich nicht unbedingt die Daten, sondern die Technologie. Für mich (und das ist meine persönliche Meinung) liegt die Schwierigkeit der Schichtentrennung in den Schnittstellen zwischen den einzelnen Schichten, und genau das wird in dem Beispiel meiner Meinung nach falsch dargestellt, aber das ist ein allgemeines Problem bei WinForms, bzw. Binding an DataSources.


Aber ich habe gerade ein -auf den ersten Blick- sehr vielversprechendes Projekt gefunden:
https://github.com/JonPSmith/SampleMvcWebApp

Das werde ich mir mal genauer anschauen
private Nachricht | Beiträge des Benutzers
Palin
myCSharp.de - Member



Dabei seit:
Beiträge: 1115

beantworten | zitieren | melden

Also das Beispiel ist daraus entstanden, das es für bei kleineren Anwendungsfallen zu Aufwändig wäre eine 3-Schichten Architektur zu verwenden. Ich denke es zeigt das eine 3-Schichten Architektur recht einfach erstellt werden kann. Es bedeutet ja erst mal nur das PL,BL und DAL trenne und das ich von höheren Schichten nur auf niedrige zugreife und niedrige Schichten höhere nicht kennen.

Wie die Schichten jetzt mit einander Kommunizieren und was am sinnvollsten wo macht, ist ein ganz anderes Thema. Und hängt einfach von zu vielen Faktoren ab um da eine pauschale Aussage geben zu können.

Um ein paar Beispiele zu nennen.
Technologie
Wenn ich in WPF eine lang laufende Berechnung im BL hab, ist es am einfachsten wenn er mich über ein Event über den Fortschritt informiert.
Hab ich ein ASP MVC Seite funktioniert es nicht wenn die auf dem Server läuft. Da kann ich dann z.B. SignalR nehmen.

Aufgabe (was macht die Anwendung)
Wenn man BL einfach 2 Zahlen zusammen Rechnet, brauche ich kein Event. Da übergebe ich, die Zahlen an eine Methode und die liefert mir ein Ergebnis zurück.

Rahmen Bedingungen
Wir haben bei uns noch Teils ini Dateien, die Verwendet werden müssen. Die Datei wollte ich auf keinen falle in den BL geben und da dann auf ein Objekt Mappen. Also hab ich sie im DAL auf das Objekt gemappt und der DAL hat mir dann das Objekt zurückgeben. Damit war meine Schnittstelle für den DAL definiert und da ich die gleiche Schnittstelle auch beim SQL Server verwenden will. Bleibt mir einstündlich anderes Übrige als das Objekt da zu Mappen.


Eine Lösung die immer Richtig ist gibt es da nicht.
Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16112

beantworten | zitieren | melden

Zitat von Palin
Es bedeutet ja erst mal nur das PL,BL und DAL trenne und das ich von höheren Schichten nur auf niedrige zugreife und niedrige Schichten höhere nicht kennen.
Streng genommen greifst Du aber laut Deiner Beschreibung von Unten nach Oben (DAL auf BL).

Zitat von Palin
Wie die Schichten jetzt mit einander Kommunizieren und was am sinnvollsten wo macht, ist ein ganz anderes Thema.
Eigentlich ist das klar.
Zitat von Palin
Wenn ich in WPF eine lang laufende Berechnung im BL hab, ist es am einfachsten wenn er mich über ein Event über den Fortschritt informiert.

Hab ich ein ASP MVC Seite funktioniert es nicht wenn die auf dem Server läuft. Da kann ich dann z.B. SignalR nehmen.

Das macht kein Unterschied in der BL. Die BL kann in beiden Fällen ein Event anbieten.
In der WPF UI wird eben der Event abonniert. Über ASP.NET eben ein WebSocket-Message angetriggert.
Das ist damit nur ein Customizing der UI, nicht der BL.
Völlig Schichtkonform.
Zitat von Palin
Wenn man BL einfach 2 Zahlen zusammen Rechnet, brauche ich kein Event. Da übergebe ich, die Zahlen an eine Methode und die liefert mir ein Ergebnis zurück.
Willst Du auf asynchon vs. synchron hinaus?
Auch das ist völlig getrennt von der UI.
Zitat von Palin
Wir haben bei uns noch Teils ini Dateien, die Verwendet werden müssen. Die Datei wollte ich auf keinen falle in den BL geben und da dann auf ein Objekt Mappen.

Ich kenn den Inhalt nicht genau. Aber das ist bei Dir kein Einzelfall - ich hab auch noch tagtäglich aufgrund meiner Nähe zum Maschinenbau damit zutun.

Beispiel:
Wir haben eben ein IUserRepository.
Wenn der User aus der Datenbank kommen soll, dann haben wir eine UserMssqlRepository Implementierung. Wenn der User aus der Ini kommen soll eine UserIniRepository Implementierung.

Ich sehe nicht, wieso Dein Fall so einzigartig ist, weshalb er das Konstrukt "verletzen" müsste.
Ist für mich eher ein OOP Thema als ein Architektur-Thema.
Zitat von MorphieX
Aber ich habe gerade ein -auf den ersten Blick- sehr vielversprechendes Projekt gefunden:
https://github.com/JonPSmith/SampleMvcWebApp
Ich mag das Beispiel aus einem Grund (pauschal) nicht: es wird angenommen, dass Entität und Business Model den gleichen Inhalt hat.
Das ist wieder ein unreales Beispiel für größere, komplexe Anwendungen.

Aus aktuellem Anlass: ich finde den Artikel Softwarearchitektur ohne Grundsätze sehr gut.
Es steckt sehr viel Wahrheit drin.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
Palin
myCSharp.de - Member



Dabei seit:
Beiträge: 1115

beantworten | zitieren | melden

Zitat von Abt
Zitat von Palin
Es bedeutet ja erst mal nur das PL,BL und DAL trenne und das ich von höheren Schichten nur auf niedrige zugreife und niedrige Schichten höhere nicht kennen.
Streng genommen greifst Du aber laut Deiner Beschreibung von Unten nach Oben (DAL auf BL).

Mache ich garantiert nicht. Da ich für die einzelnen Sachen eigene Assemblys habe. Und die sich sonst nicht Kompilieren ließen. (An den Punkt wäre es vielleicht hilfreich, wenn ich mich da miss verständlich ausgedrückt habe. Mir zu erklären welche Punkte du nicht verstehst.).
DAL und BL greifen auf eine Modell Klasse (Assembly) zu. Die Modell Klasse kennt weder DAL noch BL. Man könnte sich also als 4. Layer bezeichnen.

Zitat von Abt
Das macht kein Unterschied in der BL. Die BL kann in beiden Fällen ein Event anbieten.
In der WPF UI wird eben der Event abonniert. Über ASP.NET eben ein WebSocket-Message angetriggert.
Das ist damit nur ein Customizing der UI, nicht der BL.
Völlig Schichtkonform.
Ja da könnten wir jetzt anfangen zu Diskutier. Was noch mit zum BL und was zu UI gehört. Wollen wir uns da drauf einigen. Das das Frontend was im Brauers Läuft. Nicht einfach ein Event Verwenden kann wie es eine WPF Anwendung kann.

Zitat von Abt
Willst Du auf asynchon vs. synchron hinaus?
Auch das ist völlig getrennt von der UI.
Nein ich möchte eigentlich nur (wie Erwähnt) ein paar Beispiele Aufzeigen. Wie so man nicht einfach eine Beispiel Implementierung für ein 3 Schichten Model bereitstellen kann.
Zitat von Abt
Wir haben eben ein IUserRepository.
Wenn der User aus der Datenbank kommen soll, dann haben wir eine UserMssqlRepository Implementierung. Wenn der User aus der Ini kommen soll eine UserIniRepository Implementierung.
Hey das haben wir auch. Und jetzt erkläre mir mal bitte wie der BL die Entitiy aus dem UserMssqlRepository Mappen sollen. Die gar nicht in dem UserIniRepository enthalten sind?

Zitat von Abt
Ich sehe nicht, wieso Dein Fall so einzigartig ist, weshalb er das Konstrukt "verletzen" müsste.
Ist für mich eher ein OOP Thema als ein Architektur-Thema.
Bitte erläutere mir es mal näher. Mir ist aktuell nicht klar was ich verletzen sollte. Grundlagen der Schichten Architektur verletze ich nicht. SOLID, DRY, YAGNI usw. Verletze ich nicht. Was ich sicher Verletze ist, irgend ein Ungeschriebene Prinzip, das ich die Selbe Klasse für die Datenhaltung nicht im DAL,BL und PL verwenden darf. Gut bei einem Logger darf ich das.
Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16112

beantworten | zitieren | melden

Zitat von Palin
Mache ich garantiert nicht.

Na doch, das sagst Du doch hier:
Zitat von Palin
DAL und BL greifen auf eine Modell Klasse (Assembly) zu. Die Modell Klasse kennt weder DAL noch BL. Man könnte sich also als 4. Layer bezeichnen.

Wenn ich ein Business Object in eine eigene DLL auslagere und auf diese von DAL und BL-Service zugreife, dann kompiliert das - wie bei Dir.
"Verletzt" aber die Idee, dass der DAL nicht das BL Model kennt.
Zitat von Palin
Da ich für die einzelnen Sachen eigene Assemblys habe.
Und die sich sonst nicht Kompilieren ließen.

Und genau das ist der Punkt. Nur weil etwas kompiliert und in anderen Assemblies liegt, hat das doch nichts mit den Layern zutun.
Die Anzahl über Projekte respektive DLLs hat null Aussagekraft. Software Schichten sind ein virtuelles und kein physikalisches Konstrukt.

I.d.R. haben meine Microservices (Beispiel) zwei:
- Identity.Usermanagement
- Identity.Usermanagement.HttpApi

In ersteren ist DAL und BL.
In zweiterem ASP.NET Core, das in Form von Json Modellen die UI darstellt.

Trotzdem sind die Schichten im Projekt "Identity.Usermanagement" sauber getrennt.

Zitat von Abt
Wollen wir uns da drauf einigen. Das das Frontend was im Brauers Läuft. Nicht einfach ein Event Verwenden kann wie es eine WPF Anwendung kann.
Nein. Der Event wird von der BL angeboten, nicht von der UI.
Ist damit etwas völlig verschiedenes. Dank WebAssembly und Blazor kann ich das exakt gleiche Event in WPF und im Frontend (> Browser) verwenden.
Exakt, 100% exakt das gleiche.

Was Du meinst ist ein Technologie-Bruch durch .NET -> JavaScript.
Das ist wieder Äpfel mit Birnen verglichen.
Zitat von Abt
Und jetzt erkläre mir mal bitte wie der BL die Entitiy aus dem UserMssqlRepository Mappen sollen. Die gar nicht in dem UserIniRepository enthalten sind?
Sorry, die Frage versteh ich nicht.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
Palin
myCSharp.de - Member



Dabei seit:
Beiträge: 1115

beantworten | zitieren | melden

Zitat von Abt
"Verletzt" aber die Idee, dass der DAL nicht das BL Model kennt.
Ich hab doch gar kein BL Model.

Zitat von Abt
Zitat von Palin
Und jetzt erkläre mir mal bitte wie der BL die Entitiy aus dem UserMssqlRepository Mappen sollen. Die gar nicht in dem UserIniRepository enthalten sind?
Sorry, die Frage versteh ich nicht.
[/quote]
Du hast gesagt, das du die Entities vom DAL im BL Mappst. Entities sind aber die Objekt Darstellungen von Datenbank Tabellen (O/R Mapper). Die hast du aber in einen Ini File nicht. Wie willst du sie also Mappen.
Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern
private Nachricht | Beiträge des Benutzers