Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pw:laboratoare:05 [2023/02/24 13:14]
ciprian.dobre [Laboratorul 05: Structura Backend. Infrastructure & Core]
pw:laboratoare:05 [2023/04/24 22:36] (current)
ciprian.dobre [React Query]
Line 1: Line 1:
 ====== Laboratorul 05: Asincronicitate. Tooluri utile ====== ====== Laboratorul 05: Asincronicitate. Tooluri utile ======
-===== Scopul Laboratorului ​=====+===== Asincronicitate ​===== 
 +==== Ce este asincronitatea și de ce este importantă în React.js? ​====
  
-Incepand cu acest laborator, o sa ne concentram pe partea ​de backend ​proiectului. Asa cum s-prezentat anteriorpartea de backend ​se axeazain principal, la implementarea logicii de domeniu a sistemului+Asincronicitatea este capacitatea unei aplicații ​de a rula multiple procese în paralel, fără ​să fie blocată de procesele care durează mai mult sau care necesită ​așteptare. În React.jsasincronicitatea este importantă pentru că aplicațiile sunt construite în mare parte în jurul evenimentelor și a datelor care se schimbă în timp real. Prin urmareeste important să putem rula procese asincrone în timp ce aplicația rămâne responsive.
  
-Asadarbackend-ul ​pe care il vom dezvolta va oferi suport ​pentru ​actiunile deja prezentate atat in arhitecturacat si in mockups si frontend.+De exempluatunci când facem o cerere către o bază de date pentru a obține informații pentru a afișa ​pe o pagină, aceasta poate dura ceva timp. În acest timp, aplicația nu ar trebui să fie blocată sau să înceteze să funcționeze. În schimb, React.js poate utiliza asincronicitatea ​pentru ​a rula procesul în fundalfără să blocheze restul aplicației.
  
-Scopul acestui prim laborator este sa va familiarizati cu C#, .NET 6 si cu Visual StudioIn plusva vom oferi structura backendului,​ asa cum am gandit-noica sa reflecte cat mai bine informatiile predate la curscat si standarde din industrie.+În plusasincronicitatea este importantă în React.js pentru a gestiona evenimente și animații în timp realDe exempluatunci când utilizatorul face clic pe un buton pentru a efectua ​acțiuneaplicația trebuie să răspundă rapid pentru a da utilizatorului feedback. Prin utilizarea asincronicitățiiaplicația poate rula procesul în fundal fără a bloca restul aplicației și fără a încetini răspunsul.
  
-===== Ce veti invata la acest laborator? ===== +În concluzie, asincronicitatea este importantă pentru a asigura o performanță bună și o experiență de utilizare plăcută. Prin rularea proceselor asincrone, aplicația poate rămâne responsive și poate gestiona evenimente și date în timp real.
  
-In urma parcurgerii acestui laborator veti dobandi o privire ​de ansamblu asupra lucrului in ecosistemul .NET si a modului in care se poate structura un backend.+==== Utilizarea ​de API-uri asincrone în React.js ====
  
-===== Suita de tehnologii folosita =====+În React.js, putem utiliza API-uri asincrone pentru a obține și a procesa date de la servere externe. Acest lucru poate fi făcut folosind funcții precum fetch(), axios sau XMLHttpRequest pentru a efectua cereri HTTP către un server. API-urile asincrone sunt esențiale în dezvoltarea aplicațiilor moderne, deoarece permit obținerea de date în timp real fără a fi nevoie de încărcarea unei întregi pagini.
  
-Pentru ​dezvoltarea acestui backend vom folosi ​urmatoarele:​+Pentru ​a utiliza API-uri asincrone în React.js, trebuie să creăm o componentă care să utilizeze o astfel de funcție. O abordare comună este de a utiliza fetch() pentru a efectua o cerere GET către server pentru a obține datele. În momentul în care datele sunt primite, putem folosi ​metoda setState() pentru a actualiza starea componentei cu noile date.
  
-  * .NET 6 (C#) - pentru cod +<​code>​ 
-  * SQL Server Express - pentru baza de date +import React, { Component } from '​react';​
-  * Postman - pentru testarea codului +
-  * Visual Studio - pentru scrierea codului+
  
-<note important>​Visual Studio, nu Visual Studio Code!</​note>​ +class MyComponent extends Component { 
-===== Lucruri necessare pentru parcurgerea laboratorului ====== +  ​constructor(props) { 
-==== Cunostinte necesare ====+    ​super(props);​
  
-In primul randtrebuie sa aveti bine pusa la punct teoria si practica discutata la urmatoarele materii+    this.state = { 
-  * Programare Orientata pe Obiecte ​ +      data: [], 
-  * Baze de Date +      isLoadingtrue, 
-  ​* Protocoale de Comunicatie+      ​error:​ null, 
 +    }; 
 +  ​}
  
-Motivele sunt urmatoarele:+  componentDidMount() { 
 +    fetch('​https://​jsonplaceholder.typicode.com/​todos'​) 
 +      .then(response => response.json()) 
 +      .then(data => this.setState({ data: data, isLoading: false })) 
 +      .catch(error => this.setState({ error, isLoading: false })); 
 +  }
  
-  ​* C# este un limbaj OOP +  ​render() { 
-  * Aveti nevoie sa stiti BD pentru ca vom folosi BD +    const { data, isLoading, error } = this.state;
-  * Aveti nevoie sa stiti cum functioneaza protocolul HTTP+
  
-==== Unelte necesare ====+    if (error) { 
 +      return <​p>​{error.message}</​p>;​ 
 +    }
  
-  * Visual Studio ​(de preferat [[https://​visualstudio.microsoft.com/​thank-you-downloading-visual-studio/?​sku=Community&​channel=Release&​version=VS2022&​source=VSLandingPage&​cid=2030&​passive=false|2022 community edition]]+    if (isLoading{ 
-  * [[https://​www.postman.com/downloads/?​utm_source=postman-home|Postman]] sau orice alt fake-client +      ​return <​p>​Loading...</p>; 
-  * [[https://​dotnet.microsoft.com/​en-us/​download/​dotnet/​6.0|.NET 6 SDK]] +    }
  
-==== Suportul de laborator ==== +    return ( 
- +      <div
-Codul poate fi accesat pe [[https://​gitlab.com/​tehnologiiweb/​book-library-v1/​-/​tree/​main/​BookLibrary|linkul nostru de gitlab]]. +        <h1>Lista de sarcini:</h1
- +        <ul
-==== Teams ==== +          {data.map(task ​=
- +            <li key={task.id}>{task.title}</li> 
-Asa cum am spus la inceputul anului, aveti un canal special dedicat pentru **Discutii de laborator**. Orice intrebare aveti cu privire la laborator, va rog sa o puneti acolo, pentru a putea sa va lamurim. +          ))} 
-===== Ce este un backend? ====== +        </ul
- +      </div
-Multa lume considera ca backendul este pur si simplu o colectie de rute expuse de un server, prin care acesta primeste informatii si furnieaza informatii. Pe scurt, un blackbox. +    );
- +
-<note important+
-Aceasta definitie nu este gresita, dar nu este nici complet corecta, deoarece supra-simplifica rolul unui backend.</note> +
- +
-Un backend este acea componenta software dintr-o aplicatie care coordoneaza **business logic**-ul unei aplicatii. Chiar daca utilizatorii interactioneaza cu frontendul, frontendul poate efectua doar //actiunile care sunt permise ​de backend//. Asadar, backendul reprezinta **nucleul logicii de domeniu** a oricarei aplicatii. +
- +
-Backendurile pot fi scrise in multe moduri. Noi ne vom axa pe clasicul **Web API REST**. +
- +
-<note tip>Un Web API este un server web ce expune rute prin care clientii pot interactiona cu sistemul.</note+
-<note tip>Un Web API REST (Representational State Transfer) este un Web API care comunica peste HTTP.</​note+
- +
- +
-===== Structura Backendului ====== +
- +
-Cea mai simpla structura pe care puteam sa o folosim este **3-tier architecture**Asa cum a fost prezentat la curs, este cea mai simpla de folosit, dar si cea mai simpla de transformat in dezastru. +
- +
-Am ales sa construim codul folosind o combinatie intre [[https://​docs.microsoft.com/​en-us/​dotnet/​architecture/​modern-web-apps-azure/​common-web-application-architectures|Clean Architecture]],​ [[https://​docs.microsoft.com/​en-us/​azure/​architecture/​microservices/​model/​tactical-ddd|Tactical Domain Driven Design]] si [[https://​www.youtube.com/​watch?​v=cVVMbuKmNes&​ab_channel=CodeOpinion|Vertical Slicing]]. Motivatia pentru fiecare alegere este urmatoarea:​ +
- +
-  * **Clean** - ofera o separatie clara intre nivele si, cu toate astea, dependentele sunt curate. Nivelul **core** nu are depedenta fata de nimeni. Nivelul **infrastructure** are dependenta de **core**. Nivelul **application** are depdendenta de **core** si **infrastructure**. +
-  * **Tactical Domain Driven Design** - deoarece folosim deja Clean, logica de domeniu este scrisa in Core. Am utilizat sabloane expuse de Tactical DDD (precum agregate), pentru a reflecta ce am discutat pana acum. +
-  * **Vertical Slicing** - ofera un grad de readability foarte mare codului. Cu toate astea, am utilizat Vertical Slicing doar in nivelul **application**,​ pentru a segrega cat mai bine functionalitatile. +
- +
-<note important>​Pentru acest backend, care este simplu, arhitectura pe care noi am propus-o este **overkill**. Cu toate astea, am luat aceasta decizie cu scopul de a va oferi un material didactic cat mai cuprinzator.</​note>​ +
- +
-===== Organizarea codului ===== +
- +
-Fiecare nivel din **Clean** este reprezentat de cate un proiect de .NET **(.csproj)**. Proiectele sunt toate scrise sub umbrela unei solutii .NET **(.sln)**. +
- +
-{{:​pw:​laboratoare:​solutionexplorer.png?800|}+
- +
-<note tip>Crearea unei solutii noi este triviala</​note>​ +
- +
-Pentru a adauga un proiect nou intr-o solutie existenta, click dreapta pe solutie -> Add New Project +
- +
-{{:​pw:​laboratoare:​solutionexploreraddnewproject.png?800|}+
-==== Organizarea Nivelului Core ==== +
- +
-Nivelul **Core** este cel mai important, deoarece descrie logica de domeniu din proiect. Aici reprezentam toate deciziile de business care se iau in aplicatia noastra. Este primul nivel pe care l-am scris. +
- +
-{{:​pw:​laboratoare:​solutionexplorercore.png?​800|}} +
- +
-Observati ca avem 3 foldere principale:​ +
-  * **DataModel** - contine clasele care descriu **Entitatile** sistemului. Aceste clase sunt folosite atat in **Domain**, cat si in nivelul **Infrastructure** +
-  * **Domain** - contine logica de business, impartita intre cele 2 agregate, **Book** si **UserRentals**. Aici avem definite urmatoarele:​ +
-    * clasele de domeniu (de ex: //​BooksDomain.cs//​) +
-    * interfetele pentru repository-uri (de ex: //​IBooksRepository.cs//​) +
-    * comenzi (de ex: //​InsertBookInLibraryCommand.cs//​) +
-    * evenimente (de ex: //​UserRentedBookEvent.cs//​) +
-    * exceptii de domeniu (de ex: //​RentalNotFoundException.cs//​) +
-  * **[[https://​martinfowler.com/​bliki/​Seedwork.html|SeedWork]]** - contine interfete si clase reutilizabile,​ utile pentru "​standardizarea"​ codului. +
- +
-=== Seed Work === +
- +
-Clasele si interfatele scrise in **SeedWork** sunt folosite pentru a //limita libertatea programatorului//​ si pentru a reduce //​duplicarea codului//. Chiar daca are conotatie negativa, acest lucru aduce beneficii, deoarece standardieaza modul in care cod nou este adaugat in aplicatie. Deoarece C# este un limbaj OOP, am profitat la maxim de capabilitatile de mostenire si genericitate pe care ni le ofera. +
- +
-{{:​pw:​laboratoare:​solutionexplorerseedwork.png?​500|}} +
- +
-**Entity** reprezinta o clasa de baza pentru entitatile din baza de date. In aceasta clasa avem definite cheia primara (Id), si 2 campuri pentru audit: createdAt si updatedAt. +
- +
-<code c#> +
-public abstract class Entity +
-+
-  public int Id { get; set; } +
-  public DateTime CreatedAt { get; set; } +
-  public DateTime UpdatedAt { get; set; } +
-}  +
-</code+
- +
-<note important>​Entity Framework entities, nu DDD entities.</note+
-<note tip>​[[https://​docs.microsoft.com/​en-us/​ef/​|Entity Framework]] este un framework de gestiune si interactiune a bazelor de date, pentru aplicatiile scrise in .NET. O sa vedeti mai multe detalii la nivelul **Infrastructure**.</​note>​ +
- +
-**IAggregateRoot** este o interfata goala, cu rol de **[[https://​en.wikipedia.org/​wiki/​Marker_interface_pattern|marker]]**. Aceasta va marca obiectele care sunt AggregateRoot (Tactical DDD)+
- +
-<code c#> +
-public interface IAggregateRoot +
-+
-+
-</​code>​ +
- +
-**DomainOfAggregate** este o clasa abstracta, care primeste ca argument generic un IAggregateRoot. Rolul sau este sa fie un **blueprint** pentru cele doua obiecte de domeniu cu care lucram, **BooksDomain** si **UsersRentalsDomain**. +
-<code c#> +
-public abstract class DomainOfAggregate<​TAggregate>​ where TAggregate : Entity, IAggregateRoot +
-+
-  private protected readonly TAggregate aggregate;​ +
- +
-  public DomainOfAggregate(TAggregate aggregate) +
-  { +
-    this.aggregate = aggregate;+
   }   }
 } }
-</​code>​ 
-<note tip>Dupa cum observati, aceasta clasa abstracta doar primeste, in constructor,​ ca parametru, ceva de tipul argumentului generic si il retine pentru acces ulterior.</​note>​ 
  
-**ICreateAggregateComand** este o interfata **marker** pentru comenzile care creaza obiecte de domeniu. +export default MyComponent;​
-<code c#> +
-public interface ICreateAggregateCommand +
-+
-}+
 </​code>​ </​code>​
  
-**IRepositoryOfAggregate** este interfata genericace accepta ca argumente ​un **IAggregateRoot** si o **ICreateAggregateCommand**Aceasta interfata expune ​cele 3 metode ​de care avem nevoie in repository:+<​note>​În acest exemplu, am utilizat fetch() pentru a obține ​listă de sarcini de pe serverul JSONPlaceholder. Am actualizat starea componentei cu datele obținute din server utilizând metoda setState(). Dacă întâmpinăm o eroare în timpul procesului de obținere a datelorafișăm ​un mesaj de eroareÎn cele din urmă, am afișat lista de sarcini printr-o mapare a datelor primite într-o listă.</​note>​
  
-  * sa cream un agregat +În concluzie, utilizarea API-urilor asincrone în React.js este esențială pentru a obține date din servere externe și pentru a actualiza componentele în timp real fără a fi nevoie de încărcarea unei întregi pagini. 
-  * sa returnam un agregat +===== Tooluri si biblioteci utile =====
-  * sa salvam starea sistemului+
  
-<code c#> +==== Material UI ====
-public interface IRepositoryOfAggregate<​TAggregate,​ TAggregateCreateCommand>​  +
-  where TAggregate : Entity, IAggregateRoot +
-  where TAggregateCreateCommand : ICreateAggregateCommand +
-+
-  Task AddAsync(TAggregateCreateCommand command, CancellationToken cancellationToken);​ +
-  Task<​DomainOfAggregate<​TAggregate>?>​ GetAsync(int aggregateId,​ CancellationToken cancellationToken);​ +
-  Task SaveAsync(CancellationToken cancellationToken);​ +
-}  +
-</​code>​+
  
-<note tip>​Observati cum functionalitatea acestei interfete ​este conturata de Agregatul oferit si de Comanda oferita.</​note>​ +Material UI este o bibliotecă ​de componente pentru React.js, care oferă ​colecție de componente UI pre-stilizate pentru a ajuta la crearea ​de interfețe utilizator moderne și atractiveAceste componente sunt bazate pe stilurile și principiile ​de design ale Google Material Design, care au fost dezvoltate pentru a oferi un aspect coerent și familiar aplicațiilor web și mobile.
-<note tip>In aplicatiile ​care nu folosesc DDD, sa vedeti ca repository-urile au mult mai multe metode. De fapt, fiecare acces la baza de date este mediat de un apel al repositoryIn cazul nostru, nu avem nevoie ​de asa ceva, deoarece obiectul de domeniu are acces la entitate si ii poate modifica starea. Are nevoie doar sa salveze starea sistemului.</​note>​ +
-<note tip>Task reprezinta o actiune asincrona, care se intampla pe un thread. Este o abstractizare a lucrului cu threaduri. Un task poate fi asteptat (**await**). Mai multe informatii, [[https://​docs.microsoft.com/​en-us/​dotnet/​api/​system.threading.tasks.task?​view=net-6.0|gasiti aici.]]</​note>​+
  
-=== Data Model ===+Material UI oferă o varietate de componente, inclusiv butoane, casete de selectare, câmpuri de text, tab-uri, bare de navigare, bare laterale, ferestre modale, diagrame și multe altele. Aceste componente sunt ușor de utilizat și oferă o varietate de opțiuni de personalizare pentru a se potrivi cu designul și aspectul dorit.
  
-Aici am definit clasele ​care reprezinta datele din sistemul nostruAceste clase sunt simplefara functionalitate. Ele vor fi folosite ​de sistemul de configurare al **EntityFramework**.+Material UI oferă, de asemenea, o varietate de teme predefinite și opțiuni de personalizare, ​care permit utilizatorilor să își personalizeze aspectul aplicației în funcție de nevoile și preferințele lorDe exempluputeți schimba culorile ​de accent, fonturile, dimensiunile și multe altele.
  
-In plus, entitatile DDD mostenesc clasa abstracta **Entity**in timp ce radacinile ​de agregat mostenesc si interfata **IAggregateRoot**+În plus, Material UI este bine documentată și oferă o comunitate activă de utilizatori care contribuie la dezvoltarea și îmbunătățirea acesteia. De asemeneaeste compatibilă cu multe alte biblioteci și cadre de lucru populare, cum ar fi Redux, Next.js, TypeScript și multe altele. 
 +Pentru a utiliza Material UI într-un proiect React.js, trebuie să instalați mai întâi biblioteca utilizând un manager de pachete, cum ar fi npm sau yarn. După aceea, puteți importa componentele dorite în componentele React și le puteți utiliza așa cum doriți.
  
-<code c#> +De exemplupentru a utiliza un buton Material UIputeți importa componenta Buton din biblioteca Material UI și o puteți utiliza în componenta React în felul următor:
-public class Books : EntityIAggregateRoot +
-+
-  public Books(string namestring author, string genre, int maximumDaysForRental,​ bool isBooked = false) +
-  { +
-    Name = name; +
-    Author = author; +
-    Genre = genre; +
-    MaximumDaysForRental = maximumDaysForRental;​ +
-    IsBooked = isBooked; +
-  }+
  
-  public string Name { get; set; } 
-  public string Author { get; set; } 
-  public string Genre { get; set; } 
-  public int MaximumDaysForRental { get; set; } 
-  public bool IsBooked { get; set; } 
-  public virtual ICollection<​Keywords>​ Keywords { get; set; } = new List<​Keywords>​();​ 
-} 
-</​code>​ 
  
-Avem si un value object, si anume Keywords. Odata cu .NET 6, putem folosi tipul **record** pentru a descrie value objects in Entity Framework. Diferenta intre **class** si **record** este ca **record** este imutabil. +<​code>​ 
- +import ​Button } from '​@material-ui/​core'​;
-<​code ​c#+
-public record Keywords +
-{ +
-  public Keywords(string name, string description) +
-  { +
-    Name = name; +
-    Description = description;​ +
-  }+
  
-  public string Name get; init; } +function MyButton() ​
-  ​public string Description { get; init}+  ​return ( 
 +    <Button variant="​contained"​ color="​primary">​ 
 +      Apasă-mă 
 +    </​Button>​ 
 +  );
 } }
 </​code>​ </​code>​
  
-<​note ​tip>Observati diferenta intre **set** si **init**Set implica faptul ca acea proprietate poate avea valoarea schimbata in timp (mutabiltiate). Init implica faptul ca acea proprietate nu poate avea valoarea schimbata in timp (imutabilitate).</​note>​+<​note>​Aici, componenta Button este utilizată pentru a crea un buton cu aspect Material UI, care are un text "​Apasă-mă"​ și un fundal de culoare primarăUtilizând opțiunile de personalizare oferite de Material UI, puteți schimba culoarea de accent, dimensiunile și multe altele pentru a se potrivi cu designul aplicației voastre.</​note>​ 
 +==== OpenAPI Generator ==== 
 +=== Ce este OpenAPI? ===
  
-=== Domain ===+OpenAPI, anterior cunoscut sub numele de Swagger, este un set de specificații și instrumente open source care permit dezvoltatorilor să definească,​ să documenteze și să utilizeze servicii web RESTful.
  
-Cel mai important ​folder, aici avem business logic-ul aplicatiei impartit intre cele doua radicini ​de agregat**books** si **users rentals**.+<​note ​important>OpenAPI definește o formă standardizată ​de descriere a interfeței API-urilorinclusiv informații despre rutele API, parametri, tipurile de date și răspunsurile pe care le returnează serviciul.</​note>​
  
-Fiecare dintre cele doua sub foldere are toate informatiile necesare ​pentru ​desfasurarea business logic-ului aferent. Cel mai simplubooksare doar 3 clase:+Prin utilizarea OpenAPI, dezvoltatorii pot crea documentații clare și ușor de înțeles ​pentru ​serviciile lor web, ceea ce face mai ușor pentru utilizatorii acestor servicii să le utilizeze și să le integreze în propriile aplicații. De asemeneaOpenAPI permite dezvoltatorilor să genereze automat codul clientului pentru interacțiunea cu un APIceea ce poate reduce timpul și efortul necesar pentru a crea clientul API-ului.
  
-  * Obiectul ​de domeniu - BooksDomain.cs+OpenAPI este utilizat ​de multe companii mari, inclusiv Amazon, Google și Microsoft, și este susținut de comunitatea open source.
  
-<code c#> 
-public class BooksDomain : DomainOfAggregate<​Books>​ 
-{ 
-  public BooksDomain(Books aggregate) : base(aggregate) 
-  { 
-  } 
  
-  public void UpdateBook(string name, string author, string genre, int maximumDaysForRental,​ ICollection<​Keywords>​ keywords) +=== Ce este OpenAPI Generator? ===
-  { +
-    aggregate.Name ​name; +
-    aggregate.Author ​author; +
-    aggregate.Genre ​genre; +
-    aggregate.MaximumDaysForRental ​maximumDaysForRental;​ +
-    aggregate.Keywords ​keywords; +
-   }+
  
-   ​public bool BookCanBeRented(int rentalDays) => !aggregate.IsBooked && rentalDays <= aggregate.MaximumDaysForRental;​ +OpenAPI Generator este un instrument open source care permite generarea automată a codului clientului și al serverului pentru interacțiunea cu un API web, pe baza unei specificații OpenAPIAcesta poate fi utilizat cu o varietate de limbaje de programare și platforme, inclusiv Java, JavaScript, Python, Ruby, C # și multe altele.
-   ​public void MarkBookAsRented() => aggregate.IsBooked = true; +
-   ​public void MarkBookAsAvailable() => aggregate.IsBooked = false; +
-+
-</​code>​ +
-<note tip>​Observati utilizarea clasei abstracte **DomainOfAggregate**</​note>​+
  
-  * Interfata ​pentru ​repository - IBooksRepository.cs +OpenAPI Generator poate fi folosit ​pentru ​a genera codul clientului API, care poate fi inclus în aplicația dvsReact.js. Acest lucru vă permite să interacționați cu API-ul utilizând metodele și tipurile de date definite în specificația OpenAPIfără a fi necesară scrierea manuală a codului clientului.
-<code c#> +
-public interface IBooksRepository : IRepositoryOfAggregate<​BooksInsertBookInLibraryCommand>​ +
-+
-  public Task DeleteBookAsync(Books book, CancellationToken cancellationToken);​ +
-+
-</​code>​+
  
-<note tip>Pe langa metodele de baza din IRepositoryOfAggregateam mai adaugat si o metoda de stergere ​agregatei.</​note>​+În plusOpenAPI Generator poate fi utilizat și pentru ​genera codul serverului API, care poate fi folosit pentru a crea propriul dvs. API, bazat pe specificația OpenAPI. Acest lucru poate fi util dacă doriți să oferiți un API personalizat pentru propriile aplicații sau pentru a permite altor dezvoltatori să utilizeze serviciile dvs. web prin intermediul unui API.
  
-  * Comanda de adaugare ​carte in sistem - InsertBookInLibraryCommand.cs +OpenAPI Generator este disponibil ca bibliotecă Java și poate fi instalat și utilizat ca o unealtă de linie de comandă. De asemenea, există și integrări disponibile pentru diferite platforme de dezvoltare, cum ar fi Maven, Gradle, Node.js și altele.
-<code c#> +
-public record InsertBookInLibraryCommand : ICreateAggregateCommand +
-+
-  public string Name { get; init; } +
-  public string Author { get; init; } +
-  public string Genre { get; init; } +
-  public int MaximumDaysForRental { get; init; } +
-  public IEnumerable<​KeyWordDto>​ Keywords { get; init; } = new List<​KeyWordDto>​();​+
  
-  public InsertBookInLibraryCommand(string name, string author, string genre, int maximumDaysForRental,​ IEnumerable<KeyWordDtokeywords) +<note warning>​Pentru a putea utiliza OpenAPI Generator trebuie sa aveți Java instalat pe PC.</​note>
-  { +
-     Name = name; +
-     ​Author = author; +
-     Genre = genre; +
-     ​MaximumDaysForRental = maximumDaysForRental;​ +
-     ​Keywords = keywords; +
-   } +
-}+
  
-public record KeyWordDto (string Name, string Description);​ +=== Utilizare OpenAPI Generator ===
-</​code>​+
  
-Acest fisier are definit cele doua **records** care reprezinta comanda efectiva si [[https://​en.wikipedia.org/​wiki/​Data_transfer_object|DTO]]-ul pentru Keywords.+Pentru a utiliza OpenAPI Generator într-un proiect React.js, urmați acești pași:
  
-<note tip>Observati utilizarea ICreateAggregateCommand</note>+  - Instalați OpenAPI Generator utilizând un manager de pachete precum npm sau yarn. De exemplu, pentru a instala OpenAPI Generator folosind npm, rulați următoarea comandă: ​<code>npm install @openapitools/​openapi-generator-cli -g</​code>​ 
 +  - Generați codul clientului din specificația OpenAPI folosind OpenAPI Generator. De exemplu, pentru a genera codul clientului dintr-un fișier swagger.yaml și a-l salva într-un director numit client, rulați următoarea comandă: <​code>​openapi-generator generate -i swagger.yaml -g javascript -o client</​code>​Alternativ,​ pentru scheletul de laborator, porniți BE-ul si folosiți comanda:<​code>​openapi-generator-cli generate -i http://​localhost:​5000/​swagger/​v1/​swagger.json -g typescript-fetch -o .\src\Api\ --additional-properties=supportsES6=true</code> 
 +  - În interiorul componentelor React.js, importați funcțiile generate și utilizați-le în mod corespunzător.
  
-<note tip>In folderul pentru **UsersRentals** avem mai multe clase definite, deoare logica de business este putin mai complexa. Clasele care sunt in plus reprezinta exceptii si evenimente.</​note> ​ 
  
-==== Organizarea nivelului Infrastructure ==== 
  
-Dupa ce am terminat de scris nivelul core, am scris nivelul de infrastructura. Rolul acestui nivel este sa se ocupe de infrastructura proiectului,​ precum baze de date, sisteme de logging, etc.... 
  
-In cadrul proiectului nostru, in acest nivel ne ocupam doar de baza de date, adica de configurarea entitatilor (de EF) si de implementarea celor 2 repositories. 
  
-Asa cum am spus initial, singura depedenta pe care o are nivelul de infrastructura este asupra nivelului Core: 
-  * Entitatile (de EF, nu DDD) declarate 
-  * Interefetele pentru repositories 
  
-{{:​pw:​laboratoare:​solutionexplorerinfrastructure.png?​600|}}+==== React Query ====
  
-=== BookLibraryContext ​===+=== Ce este React Query? ​===
  
-Cel mai important fisier din acest nivel, reprezinta ​implementare a **DbContext**, ​care reprezinta clasa esentiala ​de functionare ​**EntityFramework**.+React Query este bibliotecă pentru React.js ​care oferă o modalitate simplă ​de a gestiona starea și datele asincrone dintr-o aplicație React. Această librărie folosește un concept numit "​queries"​ pentru a face apeluri la server și pentru a gestiona datele returnate. React Query utilizează cache-ul local pentru a reduce numărul de cereri la server și pentru a oferi o experiență mai rapidă utilizatorilor.
  
-<code c#> +React Query oferă, de asemenea, o serie de caracteristici utile, cum ar fi stale-while-revalidate ​(SWR), care permite afișarea imediată a datelor din cache, chiar și înainte de a fi actualizate cu datele din server, și focus pe performanță,​ care permite amânarea încărcării datelor până când componenta respectivă este încărcată.
-public class BookLibraryContext : DbContext +
-+
-  public BookLibraryContext(DbContextOptions<​BookLibraryContext>​ options: base(options)  +
-  { +
-  }+
  
-  protected override void OnModelCreating(ModelBuilder modelBuilder) +<note important>​În esență, React Query face mai ușor pentru dezvoltatori să gestioneze datele și starea în aplicații React, permițându-le să se concentreze mai mult pe construirea interfeței și mai puțin pe gestionarea datelor.</​note>​
-  { +
-    modelBuilder.ApplyConfigurationsFromAssembly(typeof(BooksConfiguration).Assembly);​ +
-  }+
  
-  public DbSet<​Users>​ Users => Set<​Users>​();​ +=== Uilizare React Query ===
-  public DbSet<​Books>​ Books => Set<​Books>​();​ +
-  public DbSet<​Rentals>​ Rentals ​=> Set<​Rentals>​();​ +
-+
-</​code>​+
  
-Contextul va fi folosit in nivelul **Api** pentru ​interactiona cu baza de date in cazul citirilorcat si nivelul **infrastructure** in implementarea repository-urilor.+  - Pentru ​începe să utilizați React Querytrebuie să îl instalați mai întâi. Puteți face acest lucru utilizând comanda de instalare npm: <​code>​npm install @tanstack/​react-query</​code>​ 
 +  - După ce ați instalat React Query, trebuie să îl importați și să îl configurați în aplicația voastrăReact.js. În mod tipic, acest lucru se face în fișierul App.js. Iată un exemplu simplu: <​code>​import React from '​react';​ 
 +import { QueryClient,​ QueryClientProvider } from '​react-query';​
  
-<note tip>​Observati cum clasele pe care le-am marcat in **Core** ca Entities sunt transformate aici in **DbSet**. DbSet este o clasa a EntityFramework.</​note>​+const queryClient = new QueryClient();​
  
-=== Entity Configurations ===+function App() { 
 +  return ( 
 +    <​QueryClientProvider client={queryClient}>​ 
 +      <div className="​App">​ 
 +        {/* Restul componentelor tale */} 
 +      </​div>​ 
 +    </​QueryClientProvider>​ 
 +  ); 
 +}
  
-Aici am definit configuratiile ​pentru ​baza de date, sub forma de clase de configurareAceste clase de configurare sunt interpretate de **EntityFramework**,​ datorita ​<​code ​c#>modelBuilder.ApplyConfigurationsFromAssembly(typeof(BooksConfiguration).Assembly);</​code>​ scris in context.+export default App; 
 +</​code>​ 
 +  - După ce ați configurat React Query, puteți începe să utilizați useQuery și useMutation hooks pentru ​a obține și a actualiza dateleIată un exemplu ​de utilizare a useQuery: ​<​code>​import React from '​react';​ 
 +import { useQuery } from '​react-query'​;
  
-O clasa de configurare trebuie sa mosteneasca **IEntityTypeConfiguration** pentru o anumita entitate si sa implementeze metoda **Configure**.+function App() { 
 +  const { isLoading, error, data } = useQuery('​todos',​ () => 
 +    fetch('​https://​jsonplaceholder.typicode.com/​todos'​).then((res) => 
 +      res.json() 
 +    ) 
 +  );
  
-<note tip>​Acest mod de configurare poarta numele de **Fluent API**Mai multe informatii puteti gasi [[https://​www.learnentityframeworkcore.com/​configuration/​fluent-api|aici]].</​note>​+  if (isLoading) return '​Loading...';
  
-<code c#> +  if (errorreturn `An error has occurred: ${error.message}`;
-internal class BooksConfiguration : EntityConfiguration<​Books>​ +
-+
-  public override void Configure(EntityTypeBuilder<​Books>​ builder) +
-  ​{ +
-    builder +
-      ​.Property(x => x.Name) +
-      .IsRequired();+
  
-    builder +  return ( 
-      .Property(=> x.Genre+    <div> 
-      ​.IsRequired();+      ​<​h1>​Todos</​h1>​ 
 +      <​ul>​ 
 +        {data.map((todo) ​=> 
 +          <li key={todo.id}>​{todo.title}</​li>​ 
 +        ))} 
 +      ​</​ul>​ 
 +    </​div>​ 
 +  ​); 
 +}
  
-    builder +export default App;
-      .Property(x => x.Author) +
-      .IsRequired(); +
- +
-    builder +
-      .OwnsMany(x => x.Keywords);​ +
- +
-    base.Configure(builder);​ +
-        } +
-    }+
 </​code>​ </​code>​
  
-<​note ​tip>Deoarece trebuie sa configuram si clasa de baza **Entity**, definita in SeedWork, configuratiile noastre mostenesc, de fapt,  +<​note>​În acest exempluam utilizat useQuery pentru a obține o listă ​de to-do-uri ​de la un API extern. useQuery primește un identificator pentru query ('​todos'​ în acest cazși o funcție de interogare care va fi apelată pentru a obține dateleDacă query-ul este încărcat ​(isLoading), va fi afișat un mesaj "​Loading...", dacă apare o eroare ​(error), va fi afișat un mesaj de eroare, altfel vor fi afișate datele.</​note>​
-**EntityConfiguration**,​ care este o clasa scrisa ​de noi</​note>​ +
-<code c#> +
-internal abstract class EntityConfiguration<​TEntity>​ : IEntityTypeConfiguration<​TEntity>​ +
-        where TEntity : Entity +
-+
-  public virtual void Configure(EntityTypeBuilder<​TEntity>​ builder) +
-  { +
-    builder +
-      ​.Property(x => x.CreatedAt) +
-      ​.ValueGeneratedOnAdd();+
  
-    builder 
-      .Property(x => x.UpdatedAt) 
-      .ValueGeneratedOnAddOrUpdate();​ 
-   } 
-} 
-</​code>​ 
-<note tip>​Observati cum am folosit IEntityTypeConfiguration impreuna cu clasa noastra Entity aici, pentru genericitate.</​note>​ 
  
-=== Repositories ​===+===== Sarcini de laborator =====
  
-Aici am definit **implementarile** interfetelor ​de repositorydeclarate in **Core**.+  - Creați un nou proiect ​de BEașa cum a fost prezentat în Laboratorul 1 
 +  - Extindeți funcționalitatea din WeatherForecastController adăugând o metoda care întoarce prognoza pentru o data primita că parametruTrebuie să întoarcă prognoza doar pentru următoarele 7 zile. În cazul în care este ceruta o data mai îndepărtată,​ trebuie întors un cod de eroare. 
 +  - Creați un nou proiect de FE, așa cum a fost prezentat în Laboratorul 4 
 +  - Instalați Material UI și React Query în proiectul nou creat 
 +  - Mapati endpointurile din BE folosind OpenAPI Generator 
 +  - Accesați endpointurile folosind componente de Material UI, React Query și metodele generate de OpenAPI Generator 
 +===== Linkuri Utile =====
  
-Asa cum am spus mai sus, repository-urile noastre doar adauga un agregat in sistem, il returneaza, si salveaza starea sistemului. 
- 
-<code c#> 
-public class BooksRepository : IBooksRepository 
-    { 
-        private readonly BookLibraryContext context; 
- 
-        public BooksRepository(BookLibraryContext context) 
-        { 
-            this.context = context; 
-        } 
- 
-        public async Task AddAsync(InsertBookInLibraryCommand command, CancellationToken cancellationToken) 
-        { 
-            var keywords = command.Keywords.Select(x => new Keywords(x.Name,​ x.Description)).ToList();​ 
- 
-            var book = new Books(command.Name,​ command.Author,​ command.Genre,​ command.MaximumDaysForRental);​ 
-            book.Keywords = keywords; 
- 
-            await context.Books.AddAsync(book,​ cancellationToken);​ 
-            await SaveAsync(cancellationToken);​ 
-        } 
- 
-        public async Task<​DomainOfAggregate<​Books>?>​ GetAsync(int aggregateId,​ CancellationToken cancellationToken) 
-        { 
-            var book = await context.Books 
-                .FirstOrDefaultAsync(x => x.Id == aggregateId,​ cancellationToken);​ 
- 
-            if (book == null) 
-            { 
-                return null; 
-            } 
- 
-            return new BooksDomain(book);​ 
-        } 
- 
-        public async Task DeleteBookAsync(Books book, CancellationToken cancellationToken) 
-        { 
-            context.Books.Remove(book);​ 
- 
-            await SaveAsync(cancellationToken);​ 
-        } 
- 
-        public Task SaveAsync(CancellationToken cancellationToken) 
-        { 
-            return context.SaveChangesAsync(cancellationToken);​ 
-        } 
-    } 
-</​code>​ 
-<note tip>​Observati utilizarea clasei de **context**.</​note>​ 
-<note tip>​Observati utilizarea claselor si interfetelor generice definite in **SeedWork**.</​note>​ 
-<note tip>​Observati utilizarea async/​await. Pentru a face **await** la un task, trebuie ca metoda sa fie decorata cu **async**.</​note>​ 
  
-=== Migrations ===+  * Documentație Materia UI: [[https://​mui.com/​material-ui/​getting-started/​overview/​]] 
 +  * OpenAPI Generator [[https://​openapi-generator.tech/​]] 
 +  * React Query [[https://​react-query-v3.tanstack.com/​]]
  
-Migrarile reprezinta un snapshot al bazei de date pe baza configurarii din cod (context + entity configurations). Vom discuta la un laborator urmator despre asta. 
pw/laboratoare/05.1677237270.txt.gz · Last modified: 2023/02/24 13:14 by ciprian.dobre
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0