Revize 0f42a3c4
Přidáno uživatelem Dominik Chlouba před téměř 4 roky(ů)
documentation/main.tex | ||
---|---|---|
1 |
\documentclass[12pt, a4paper]{thesiskiv} |
|
1 |
\documentclass[ |
|
2 |
12pt, |
|
3 |
a4paper, |
|
4 |
pdftex, |
|
5 |
czech, |
|
6 |
titlepage]{thesiskiv} |
|
2 | 7 |
\usepackage[czech]{babel} |
3 | 8 |
\usepackage[IL2]{fontenc} |
4 | 9 |
\usepackage[utf8]{inputenc} |
... | ... | |
19 | 24 |
urlcolor=black, |
20 | 25 |
bookmarksopen=true} |
21 | 26 |
|
22 |
\lstdefinestyle{mystyle}{ |
|
23 |
frame=single, |
|
24 |
commentstyle=\color{codegreen}, |
|
25 |
keywordstyle=\color{magenta}, |
|
26 |
stringstyle=\color{codepurple}, |
|
27 |
basicstyle=\ttfamily\footnotesize, |
|
28 |
breakatwhitespace=false, |
|
29 |
breaklines=true, |
|
30 |
keepspaces=true, |
|
31 |
numbers=left, |
|
32 |
numbersep=5pt, |
|
33 |
showspaces=false, |
|
34 |
showstringspaces=false, |
|
35 |
showtabs=false, |
|
36 |
tabsize=2 |
|
37 |
} |
|
38 |
|
|
39 |
\lstset{style=mystyle} |
|
40 |
|
|
41 |
\lstset{frame=tb, |
|
42 |
language=C, |
|
43 |
aboveskip=3mm, |
|
44 |
belowskip=3mm, |
|
45 |
showstringspaces=false, |
|
46 |
columns=flexible, |
|
47 |
basicstyle={\small\ttfamily}, |
|
48 |
numbers=none, |
|
49 |
breaklines=true, |
|
50 |
breakatwhitespace=true, |
|
51 |
tabsize=3 |
|
52 |
} |
|
53 | 27 |
% Začátek dokumentu |
54 | 28 |
\begin{document} |
55 | 29 |
|
... | ... | |
75 | 49 |
\tableofcontents |
76 | 50 |
|
77 | 51 |
%Kapitola1 |
78 |
\chapter{Myšlenky a funkce aplikace}
|
|
52 |
\chapter{Seznámení}
|
|
79 | 53 |
|
80 |
V rámci předmětu ASWI/KIV byla pvytvořena modulární webová aplikace pro firmu Leuze. Aplikace slouží jako prostředí pro správu cílů zaměstnanců. Na základě splnění těchto cílů, aplikace počítá .... něco s platem ....
|
|
54 |
Následující kapitoly Vás blíže seznámí s implementací webové aplikace pro správu cílů psanou pomocí technologií ASP.NET Core, Blazor Server a další. Dokument obsahuje mimo jiné základní popis použitých architektur, vzorů a způsobů pro modularitu, rozšíření, udržitelnost kódu a snadnou testovatelnost jednotlivých vrstev aplikace nezávisle na sobě. Aplikace byla vytvořena za pomocí .NET 5.0.
|
|
81 | 55 |
|
56 |
\chapter{Architektura a použité vzory} |
|
82 | 57 |
|
58 |
\section{ExtCore Framework} |
|
59 |
|
|
60 |
ExtCore umožňuje vytvářet webové aplikace z hlediska nezávislých přepoužitelných modulů či rozšíření. Každý z těchto modulů se skládá z jednoho či více ASP.NET Core projektů. Jakýkoliv project založení na ExtCore framework dokáže načíst a identifikovat třídy a objekty definované v jakékoliv importované knihovně (volitelně například pomocí predikátů pro filtrování) a následné získání jejich instancí. Libovolný modul nebo rozšíření během inicializace a spuštění webové aplikace spusti svůj vlastní kód. Pomocí priorit můžeme určit správné pořadí jednotlivých volání. Tuto funkcionalitu lze použít pro konfiguraci, registraci služeb atd. Jakýkoliv modul nebo rozšíření mohou spolu komunikovat pomocí událostí na straně serveru. |
|
61 |
|
|
62 |
\section{Clean architektura} |
|
63 |
|
|
64 |
V čisté architektuře se jádro aplikace dělí na dvě vrstvy. Domain vrstvu obaluje vrstva application. Ve vrstvě Domain si budeme udržovat entity („domains“) a hodnotové objekty („value objects“) naší aplikace. Jako entitu lze chápat každou třídu popisující chování objektu reálného světa, jejíž jedinečnost je určena hodnotou identifikátoru. Hodnotový objekt musíme chápat jinak. Hodnotový objekt je objekt, jehož jedinečnost je určena jeho hodnotou. Například telefonní číslo či adresa. Část jádra, nazývající se application, zastřešuje veškerou práci prováděnou s entitami či hodnotovými objekty - pravidla, neboli business rules. Jako příklad si můžeme představit například pravidlo „ulož zákazníka“. V jádře ovšem nenalezneme databázi. Nalezneme ji ve vrstvě persistence, na obrázku vyznačené zelenou barvou. Na obrázku je šipkou naznačena závislost vrstvy persistence na vrstvě application. Konkrétněji vrstva application nesmí využívat třídy definované ve vrstvě Persistence. Vrstvu persistence si v cibulové architektuře můžeme představit jako plugin do vrstvy application. V aplikační vrstvě definujeme rozhraní, které je následně ve vrstvě Persistence implementováno. Vrstva infrastructure slouží ke shlukování implementací komunikací s dalšími externími technologiemi, na kterých nechceme být závislí. Může se například jednat o technologii zodpovědnou za odesílání emailů z aplikace nebo tisk souborů atp. |
|
65 |
|
|
66 |
\section{Domain Driven Design} |
|
67 |
|
|
68 |
V kontextu vývojových fází softwaru je hlavní zaměření DDD na část analýzy a návrhu, ale zároveň zasahuje do fází sběru požadavků a implementace. Propojení těchto fází je podle DDD nutností k dosažení efektivního vývojového procesu, protože každá může odhalit detaily o doméně z jiné perspektivy. Avizuje se, že DDD není svázáno s žádnou konkrétní metodologií, ale nejvíce můžeme z těchto přístupů vytěžit v rámci agilních metodologií vývoje.\\ |
|
69 |
|
|
70 |
Pro aplikovatelnost DDD uvádí dvě podmínky: |
|
71 |
|
|
72 |
\begin{itemize} |
|
73 |
\item iterativní přístup k vývoji |
|
74 |
\item zapojení doménových expertů do procesu vývoje |
|
75 |
\end{itemize} |
|
76 |
|
|
77 |
Pod pojmem doména rozumíme oblast, kterou daný software řeší, např. bankovnictví, řízení letového provozu atd. Účelem softwaru je zefektivnit práci v této doméně. Aby toho bylo dosaženo, musí být software odrazem domény. To znamená, že musí být vybudován na hlavních konceptech a prvcích domény a vztazích mezi nimi. Doména ale existuje v reálném světě, takže ji není možné přímo přeložit do programového kódu. Proto potřebujeme vytvořit abstrakci domény – její model. Model domény nechápe DDD jako nějaký diagram, ale jako znalost o doméně, která je v tomto diagramu obsažena. Tato znalost může mít mnoho reprezentací: funkční specifikace, UML diagramy, kód programu nebo testy. Z tohoto úhlu pohledu, lze podle DDD chápat jako pět doporučení jak efektivně modelovat doménu: |
|
78 |
|
|
79 |
\begin{enumerate} |
|
80 |
\item \textbf{Provázání modelu a implementace.} Model bez možnosti implementace je v oblasti komerčního softwaru zbytečný a implementace, která není založena na modelu, snadno sklouzne k řešení problémů, které jsou mimo obor domény a opomenutí těch skutečně důležitých. |
|
81 |
\item \textbf{Model reflektuje jazyk doménových expertů.} Aby model během celého vývoje odrážel skutečnosti dané domény, je třeba používat ve všech jeho reprezentacích shodný jazyk, který vychází z jazyka doménových expertů. |
|
82 |
\item \textbf{Model zachycuje znalosti.} Objekty modelu nejsou pouze datových schématem, v modelu musíme zachytit také chování objektů a pravidla domény. |
|
83 |
\item \textbf{Destilace modelu.} Model se během vývoje stále mění, integrujeme do něj nově objevené koncepty domény a odstraňuje nebo modifikujeme ty, které se ukážou jako irelevantní. |
|
84 |
\item \textbf{Komunikace a experimentování.} Model je předmětem komunikace mezi doménovými experty a dalšími vývojáři. Tím, jak se jej snažíme popsat větami, se často objeví dosud skryté souvislosti. Prostřednictvím diskuzí a brainstormingů můžeme rychle procházet a posuzovat jeho různé variace. |
|
85 |
\end{enumerate} |
|
86 |
|
|
87 |
|
|
88 |
\section{Command-Query Responsibility Segregation} |
|
89 |
|
|
90 |
CQRS (Command-Query Responsibility Segregation) je architektonický model, který odděluje modely pro čtení a zápis dat. Základní princip je, že je možné rozdělit operace systému na dvě silně oddělené kategorie:\\ |
|
91 |
|
|
92 |
\begin{enumerate} |
|
93 |
\item \textbf{Queries.} Tyto dotazy vrátí výsledek a nezmění stav systému, a nejsou k dispozici žádné vedlejší účinky. |
|
94 |
\item \textbf{Commands.} Tyto příkazy mění stav systému. |
|
95 |
\end{enumerate} |
|
96 |
|
|
97 |
CQRS je jednoduchý koncept: Jedná se o metody v rámci stejného objektu, které jsou buď dotazy, nebo příkazy. Každá metoda buď vrátí stav nebo stav změní, ale ne obojí. I jeden objekt vzoru úložiště může odpovídat CQS. CQS se dá považovat za základní princip pro CQRS. CQRS společně s DDD jsou hlavními principy využívanými při vytváření aplikací jako mikroslužeb. Tato práce tyto principy využivá taktéž, avšak pouze v rámci monolitické aplikace. Jejich aplikováním lze zajistit snadnou udržitelnost, rozšiřitelnost a testovatelnost jednotlivých vrstev a služeb aplikace. |
|
98 |
|
|
99 |
\section{Repository pattern} |
|
100 |
|
|
101 |
Návrhový vzor pro přístup k datům, který má za úkol řešit centralizaci kódu pro přístup k datům a tím odstranit možné duplicity. Přináší podpory silně typovaných objektů. Zlepšuje udržitelnost a čitelnost kódu oddělením business logiky aplikace od logiky služeb pro přístup k datům. Důvod pro použití návrhového vzoru v řešení je odstínění přístupu k perzistentním datům pomocí Entity Framework od vyšších vrstev aplikace. Jedna konkrétní implementace repository umožnuje přístup k jednomu typu objektů daného typu T. Model aplikace nemá ponětí o tom, jakým způsobem je persistován. O to se stará právě Repository. Navíc díky tomu, že se o persistenci stará cizí objekt, stačí nám znát pouze jeho rozhranní a v případě potřeby ho snadno nahradit jiným. |
|
102 |
|
|
103 |
\section{Entity Framework} |
|
104 |
|
|
105 |
Entity Framework (EF) Core slouží pro relační mapování objektů (tzv. ORM), který: |
|
106 |
|
|
107 |
\begin{itemize} |
|
108 |
\item Umožňuje vývojářům v rozhraní .NET pracovat s databází pomocí objektů .NET. |
|
109 |
\item Eliminuje nutnost pro většinu kódu přístupu k datům, který je obvykle nutné zapsat. |
|
110 |
\end{itemize} |
|
111 |
|
|
112 |
EF podporuje následující přístupy k vývoji modelů: |
|
113 |
|
|
114 |
\begin{itemize} |
|
115 |
\item Vygeneruje model z existující databáze. |
|
116 |
\item Ruční kód modelu, aby odpovídal databázi. |
|
117 |
\item Po vytvoření modelu použijte k vytvoření databáze z modelu migraci EF . Migrace umožňují vývoj databáze jako změn modelu. |
|
118 |
\end{itemize} |
|
119 |
|
|
120 |
\newpage |
|
121 |
\begin{lstlisting} |
|
122 |
using System.Collections.Generic; |
|
123 |
using Microsoft.EntityFrameworkCore; |
|
124 |
|
|
125 |
namespace Intro |
|
126 |
{ |
|
127 |
public class BloggingContext : DbContext |
|
128 |
{ |
|
129 |
public DbSet<Blog> Blogs { get; set; } |
|
130 |
public DbSet<Post> Posts { get; set; } |
|
131 |
|
|
132 |
... |
|
133 |
} |
|
134 |
|
|
135 |
public class Blog |
|
136 |
{ |
|
137 |
public int BlogId { get; set; } |
|
138 |
public string Url { get; set; } |
|
139 |
public int Rating { get; set; } |
|
140 |
public List<Post> Posts { get; set; } |
|
141 |
} |
|
142 |
|
|
143 |
public class Post |
|
144 |
{ |
|
145 |
public int PostId { get; set; } |
|
146 |
public string Title { get; set; } |
|
147 |
public string Content { get; set; } |
|
148 |
|
|
149 |
public int BlogId { get; set; } |
|
150 |
public Blog Blog { get; set; } |
|
151 |
} |
|
152 |
} |
|
153 |
\end{lstlisting} |
|
154 |
|
|
155 |
\newpage |
|
156 |
Instance tříd entit se načítají z databáze pomocí jazyka LINQ (Language Integrated Query).\\ |
|
157 |
|
|
158 |
\begin{lstlisting} |
|
159 |
using (var db = new BloggingContext()) |
|
160 |
{ |
|
161 |
var blogs = db.Blogs |
|
162 |
.Where(b => b.Rating > 3) |
|
163 |
.OrderBy(b => b.Url) |
|
164 |
.ToList(); |
|
165 |
} |
|
166 |
\end{lstlisting} |
|
167 |
|
|
168 |
Data se vytvářejí, odstraňují a upravují v databázi s použitím instancí tříd vaší entity.\\ |
|
169 |
|
|
170 |
|
|
171 |
\begin{lstlisting} |
|
172 |
using (var db = new BloggingContext()) |
|
173 |
{ |
|
174 |
var blog = new Blog { Url = "http://sample.com" }; |
|
175 |
db.Blogs.Add(blog); |
|
176 |
db.SaveChanges(); |
|
177 |
} |
|
178 |
\end{lstlisting} |
|
179 |
|
|
180 |
Více informací ohledně Entity Framework a Entity Framework Core lze najít na \href{https://docs.microsoft.com/cs-cz/ef/core/}{Microsoft Docs}. |
|
181 |
|
|
182 |
\section{Blazor} |
|
183 |
|
|
184 |
Blazor používá komponenty. Celé to je o jejich vnořování a vkládání jejich hierarchií do stránek a layoutů. Komponenty se píšou buď přímo v C\# nebo Razoru. Pochopitelně to není ten úplně samý Razor, který znáte z Razor Pages nebo MVC, ale princip zůstává – je to předloha, ze které se vygeneruje třída komponenty. Stačí v zásadě pochopit, že blok markup kódu v .razor souboru kompiler vezme a vygeneruje z něj metodu pro vykreslení komponenty. Takže každou existující komponentu můžete vzít, zdědit z ní a přepsat její metodu vykreslení. To se hodí pro upravení již existujících komponent. Vygenerované třídy komponent jsou navíc partial, což je ideální, pokud nechcete mít klasický C\# kód u markup kódu dané komponenty – prostě použijete druhý soubor.\\ |
|
185 |
|
|
186 |
Blazor se při překreslování DOMu snaží pracovat co nejefektivněji, ale za to po vás něco chce. Všechny komponenty a elementy mají id, které vzhledem k jejich rodiči musí být unikátní, a navíc mezi jejich sourozenci ve vzestupné posloupnosti – toho se musíte držet, pokud vykreslovací funkci komponenty píšete ručně. Blazor je díky identifikátorům schopen rychle zjistit změny v DOMu a to, co může použije znovu bez nutnosti smazání starých a vytvoření nových položek stejného typu – prostě do nich znovu pošle vstupní parametry. To vás může nemile překvapit, pokud má komponenta vlastní vnitřní stav, který na vstupních parametrech nezávisí – vnitřní stav vám zůstane, ale parametry se změní na parametry komponenty, co měla toto id při minulém vykreslení (typicky v kolekcích). Naštěstí lze dát komponentám i unikátní klíč (atribut key), který zajistí, že se parametry namapují vždy na tu stejnou komponentu.\\ |
|
187 |
|
|
188 |
Blazor má bezproblémově vyřešeno jednosměrné zasílání parametrů do vnořených komponent. Buď parametr zašlete jen do přímého potomka, nebo do všech vnořených potomků (tzv. kaskádové parametry) a vnořené komponenty si je odchytí buď podle typu nebo jména. Obecně všechny události v Blazoru jsou řešeny pomocí struktury EventCallback, která zvládne synchronní i asynchronní operace a zároveň oznámí komponentě, že došlo ke změně jejího stavu a ta se poté překreslí. Při překreslení komponenty se překreslí i její vnořené komponenty, které používají nějakou její proměnnou jako vstupní parametr.\\ |
|
189 |
|
|
190 |
\chapter{Jádro aplikace} |
|
191 |
|
|
192 |
\section{Rozložení implementace} |
|
193 |
|
|
194 |
Řešení aplikace je rozděleno do několika projektů. Projekt \textit{Leuze.App} je vstupní bod aplikace, který inicializuje veškerou infrastrukturu aplikace, které dale vyřizuje požadavky jednotlivých uživatelů. Nejdůležitější částí implementace je tzv. “jádro”. Jádro se skládá z celkem 5 projektů. |
|
195 |
|
|
196 |
\textit{Leuze.Core.Application.Identity} uchovává veškeré datové modely aplikačního uživatele, tedy v našem případě především modely pro Identity Framework (uživatel, role, …) a model popisující oprávnění pro role. \textit{Leuze.Core.Application.UI} je součástí prezentační vrstvy architektury. V tomto projektu nalezneme veškeré definice UI/UX stránek jádra aplikace společně s jejich komponenty, jazykovými mutacemi atd. \textit{Leuze.Core.Application} je pak součástí business vrstvy aplikace. V tomto projektu je inicializují veškeré potřebné podpůrné záležitosti pro chod aplikace, je aktivován a nastaven ExtCore Framework, který z nakonfigurovaného adresaře načte veškeré dodatečné rozšiřující knihovny. Mimo jiné obsahuje tento projekt veškeré podpůrné třídy pro vytváření nových modulů, využívající v rámci business logiky principy CQRS.\\ |
|
197 |
|
|
198 |
Předposledním projektem jádra je \textit{Leuze.Core.Domain}. Tento projekt obsahuje veškeré doménové modely (v našem případě nazveme doménovým modelem databázovou entitu). Veškeré tyto entity jsou schopné vytvářet v systému události, která se zpropagují do příslušných vrstev a lze tak na změnu stavu modelů reagovat změnou stavu navazující.\\ |
|
199 |
|
|
200 |
Posledních projektem jádra je \textit{Leuze.Core.Infrastructure.Persistence}. Jak jeho název naznačuje, tato vrstva je stará o persistenci dat. Poskytuje tedy implementace rozhraní repozitářů definovaných v doméně aplikace, registraci doménových modelů do EF pomocí Fluent API, konfiguraci samotného modulárního databázového kontextu aplikace. Z důvodu modulárnosti aplikace bylo nutné pro podporu migrací vytvořit samostatnou třídu pro “Shared Design Time” továrnu databázového kontextu. Databázový kontext neobsahuje přímo jednotlivé DbSet “vlastnosti” (properties), o vytváření těchto setů se stará ExtCore Framework při inicializaci modulárního databázového modelu.\\ |
|
201 |
|
|
202 |
\section{Princip vytváření a implementace modulů} |
|
203 |
|
|
204 |
Již samotné jádro aplikace zavádí několik pravidel pro správné strukturování a propojení vrstev. Jednotlivé moduly se musí skládat alespoň ze čtyř projektů, aby bylo zajištěno dostatečné oddělení datové, business a prezentační vrstvy. Prvním projektem je \textit{XXX.Application.UI}, jenž poskytuje vstupní body pro jednotlivé HTTP požadavky uživatelů (obsahuje tedy hlavně kontrolery) a jejich modely. Tento projekt má přímou referenci na projekt \textit{XXX.Application}, který obsahuje výčet služeb business logiky, tedy v našem případě definování a implementace Commands a Queries pro danou doménu řešeného problému. \textit{XXX.Application} pak má přímou referenci na samotný projekt domény dané problematiky, označmě jej jako projekt \textit{XXX.Domain}. Doménový projekt obsahuje definice doménových modelů problematiky, definuje jejich události a může implementovat “handlery” pro zachycení jiných událostí definovaných v rámci celé aplikace (tzn. i na akce definované v jiných modulech). Posledním povinným projektem je \textit{XXX.Infrastructure.Persistence}, obsahující, podobně jako stejně pojmenováný projekt jádra, implementaci pro rozhraní repozitáře a registrátory entity. Je důležité upozornit, že z důvodu oddělení služeb infrastruktury nemají projekty \textit{XXX.Domain}, \textit{XXX.Application} a \textit{XXX.Application.UI} žádnou referenci na \textit{XXX.Infrastructure.Persistence}. Tímto je zajištěno oddělení datové vrstvy, která má přímou referenci na \textit{XXX.Domain} projekt. \\ |
|
205 |
|
|
206 |
Veškeré definice rozhraní služeb jsou tedy definovány v rámci \textit{XXX.Domain}. Tento projekt však nemá o jejich implementaci ponětí a jejich využívání je možné pomocí “Dependency Injection” daného rozhraní, které implementující projekt zaregistroval do poskytovatele služeb. |
|
207 |
\\ |
|
208 |
|
|
209 |
Závislosti a reference jednotlivých projektů a vrstev tak, jak byly popsány výše, je nutné dodržet pro správné oddělení jednotlivých vrstev a zajištění si správné provázanosti funkcionalit (abychom se nemuseli zabývat cyklickými závislostmi mezi projekty v řešení).\\ |
|
83 | 210 |
|
84 | 211 |
%Kapitola2 |
85 | 212 |
|
... | ... | |
114 | 241 |
V tabulce uživatelů je zobrazeno jméno, email, role, typ a senior. První tři sloupce jsou sebevysvětlující, typ je lokální nebo síťový (AD) a senior značí nadřízeného. |
115 | 242 |
Po kliknutí na Upravit je zde možnost změny jména (pouze zobrazovací), emailové adresy (pro přihlašování), role (jeden uživatel může mít pouze jednu roli) a možnost nastavení nadřízeného (lze vybrat pouze ze seznamu TL uživatelů, smazání nadřízeného se provede vybráním možnosti Vyberte uživatele) |
116 | 243 |
|
117 |
%Kapitola3 |
|
118 |
\chapter{Implementace} |
|
119 |
|
|
120 | 244 |
|
121 | 245 |
%Kapitola4 |
122 | 246 |
\chapter{Bezpečnost} |
123 | 247 |
|
124 | 248 |
|
125 |
|
|
126 | 249 |
%Kapitola5 |
127 | 250 |
\chapter{How to run} |
128 | 251 |
|
Také k dispozici: Unified diff
Updated documentation with description of application core and modularity and architectures with principles