Ndërtimi i një aplikacioni Angular që funksionon është relativisht i thjeshtë. Ndërtimi i një aplikacioni Angular që mbetet i rregullt, i lexueshëm, i shkallëzuar dhe i lehtë për t'u mirëmbajtur pas muajsh apo vitesh zhvillimi është një gjë tjetër krejtësisht. Dallimi i vërtetë midis një projekti amator dhe një projekti profesional nuk qëndron vetëm në kodin që "bën punën e tij", por në arkitekturën që lejon ekipin të vazhdojë të zhvillohet pa krijuar kaos.
Kur një aplikacion rritet, faqet, komponentët, shërbimet, thirrjet HTTP, gjendjet për të menaxhuar, lejet, rrugët, format, verifikimet dhe varësitë midis pjesëve të ndryshme të sistemit rriten. Nëse gjithçka vendoset në dosje të përgjithshme sikomponentët,shërbimetDhemodele, pas një kohe të shkurtër bëhet e vështirë të kuptosh se ku ndodhet një veçori, kush përdor çfarë dhe cilat pjesë të kodit mund të ndryshohen pa prishur pjesën tjetër.
Në këtë udhëzues ne shohim se si të strukturojmë një aplikacion modern Angular duke përdorur një të tillëarkitekturë e bazuar në veçori, një ndarje e qartë ndërmjetbërthamëDhetë përbashkëta, ikomponentë të pavarur, isinjalet, modelikomponentë inteligjentë kundër memecëdhe njëstruktura e dosjeveprojektuar për projekte reale.
Pse arkitektura ka rëndësi në Angular
Angular është një kornizë shumë e fuqishme sepse tashmë ofron një strukturë të qartë: komponentë, shërbime, injeksion varësie, rrugëzim, formularë, klient HTTP, roje, interceptor dhe shumë më tepër. Megjithatë, pikërisht për shkak se Angular ofron kaq shumë mjete, është e lehtë t'i përdorësh ato pa një strategji koherente.
Një projekt i vogël mund të mbijetojë edhe me një strukturë të organizuar keq. Megjithatë, një projekt i mesëm apo i madh ka nevojë për rregulla. Pa rregulla, secili zhvillues organizon kodin në mënyrën e vet, komponentët bëhen shumë të mëdhenj, shërbimet grumbullojnë përgjegjësi të palidhura dhe projekti ngadalë kthehet në një bllok që është i vështirë për t'u ndryshuar.
Një arkitekturë e mirë Angular duhet të ndihmojë në arritjen e disa qëllimeve themelore:
- Shkallueshmëria: Shtoni veçori të reja pa pasur nevojë të riorganizoni të gjithë projektin.
- Mirëmbajtja: Kuptoni shpejt se ku është kodi dhe si ta ndryshoni atë.
- Ndarja e përgjegjësive: Çdo skedar, komponent ose shërbim duhet të ketë një rol të qartë.
- Testueshmëria: Kodi duhet të jetë i lehtë për t'u testuar i veçuar.
- Performanca: struktura duhet të favorizojë ngarkimin dembel, tufa më të vogla dhe renderim efikas.
- Bashkëpunimi: Zhvillues të shumtë duhet të jenë në gjendje të punojnë në të njëjtin aplikacion pa shkelur gishtat e njëri-tjetrit.
Pika qendrore është kjo: arkitektura nuk shërben për të komplikuar projektin, por për ta bërë atë më të parashikueshëm. Kur një strukturë është e parashikueshme, çdo tipar i ri ka një vend të natyrshëm për të jetuar.
Arkitektura e bazuar në veçori: organizoni kodin sipas funksionalitetit
Një nga gabimet më të zakonshme në projektet Angular është organizimi i kodit sipas llojit teknik në vend të domenit funksional. Një strukturë si kjo mund të duket e rregullt në fillim:
src/app/
components/
services/
models/
pipes/
directives/
pages/
Problemi është se kjo organizatë nuk thotë asgjë për produktin. Nëse jeni duke punuar në seksionin e projekteve, do t'ju duhet të kërkoni përbërës në tëkomponentët, shërbimet nëshërbimet, modelet nëmodele, faqet nëfaqete kështu me radhë. Çdo veçori është e shpërndarë në të gjithë projektin.
Aarkitekturë e bazuar në veçorinë vend të kësaj, ai organizon kodin rreth funksionalitetit të aplikacionit. Për shembull:
src/app/
features/
dashboard/
blog/
projects/
experiences/
auth/
admin/
Çdo dosje përfaqëson një pjesë reale të produktit. Gjithçka rreth blogut është brendaveçori/blog. Gjithçka në lidhje me projektet është brendakarakteristika/projekte. Gjithçka që lidhet me administratorin është brendaveçoritë/admin.
Kjo qasje përmirëson shumë lexueshmërinë e projektit. Kur ju duhet të ndryshoni një veçori, ju e dini se ku të shkoni. Kur ju duhet të fshini një veçori, ju e dini se cilët skedarë preken. Kur një zhvillues i ri bashkohet me ekipin, ai mund ta kuptojë aplikacionin duke u nisur nga veçoritë dhe jo nga dosjet teknike të përgjithshme.
Një veçori duhet të jetë sa më autonome
Një veçori e mirë Angular duhet të përmbajë gjithçka që i nevojitet për të punuar: faqe, komponentë specifikë, shërbime të aksesit të të dhënave, modele, dyqane lokale, rrugë dhe shërbime të brendshme.
Shembull i strukturës për një veçoriprojektet:
src/app/features/projects/
projects.routes.ts
pages/
projects-list-page.component.ts
project-detail-page.component.ts
components/
project-card.component.ts
project-filters.component.ts
project-empty-state.component.ts
data-access/
projects-api.service.ts
projects-store.service.ts
models/
project.model.ts
project-filter.model.ts
utils/
project-status.util.ts
Kjo strukturë e bën veçorinë të pavarur dhe të lehtë për t'u kuptuar. Faqet janë të ndara nga komponentët më të vegjël, logjika e aksesit të të dhënave është e përfshirëqasje në të dhëna, Llojet TypeScript janë nëmodeledhe funksionet ndihmëse specifike për veçori janë nëshërbimet komunale.
Rregull i rëndësishëm: shmangni varësitë midis veçorive
Një veçori nuk duhet të importojë drejtpërdrejt komponentë, shërbime ose modele nga një veçori tjetër. Për shembull,veçori/blognuk duhet të importojë kod ngakarakteristika/projekte. Kjo krijon bashkim dhe e bën të vështirë modifikimin e një veçorie pa ndikuar te të tjerët.
Nëse dy veçori kanë nevojë për të njëjtin komponent ose mjet, ai artikull ndoshta duhet të zhvendoset nëtë përbashkëta. Megjithatë, nëse ata ndajnë logjikën e rëndësishme të domenit, mund të jetë e dobishme të krijoni një dosje ose bibliotekë të dedikuar, por gjithmonë me kufij të qartë.
Thelbi dhe i përbashkët: dallimi thelbësor
Në shumë projekte Angular gjejmë dosjebërthamëDhetë përbashkëta, por shpesh keqpërdoren. Kuptimi i ndryshimit midis këtyre dy fushave është thelbësor për ta mbajtur projektin tuaj të pastër.
Bërthama: ajo që i përket aplikacionit global
Dosjabërthamëpërmban kodin global, i përdorur në nivel aplikacioni, shpesh i inicializuar vetëm një herë. Këtu gjejmë elementë të tillë si vërtetimi, interceptorët HTTP, rojet globale, faqosja kryesore, trajtimi i gabimeve, konfigurimet, shërbimet singleton dhe logjika e infrastrukturës.
Shembull:
src/app/core/
auth/
auth.service.ts
auth.guard.ts
auth.interceptor.ts
http/
api-error.interceptor.ts
http-context.tokens.ts
layout/
main-layout.component.ts
admin-layout.component.ts
config/
app-config.token.ts
guards/
role.guard.ts
services/
logger.service.ts
storage.service.ts
Tëbërthamëai nuk duhet të bëhet një vend depozitimi për shërbime të rastësishme. Ai duhet të përmbajë vetëm atë që është vërtet globale. Nëse një shërbim shërben vetëm për veçorinë e blogut, ai nuk hynbërthamë: fut brendaveçoritë/blog/qasja në të dhëna.
E përbashkët: ajo që është e ripërdorshme dhe pa logjikë specifike biznesi
Dosjatë përbashkëtapërmban elementë që mund të ripërdoren në pjesë të shumta të aplikacionit, por jo të lidhura me një veçori specifike. Këtu gjejmë komponentë të përgjithshëm UI, tuba, direktiva, ndihmës dhe modele vërtet të përbashkëta.
src/app/shared/
ui/
button/
modal/
card/
badge/
spinner/
pipes/
truncate.pipe.ts
safe-html.pipe.ts
directives/
autofocus.directive.ts
click-outside.directive.ts
utils/
date-format.util.ts
string.util.ts
models/
pagination.model.ts
api-response.model.ts
Një komponent sibutonin e aplikacionit,aplikacion-modaloseapp-spinermund të qëndrojë brendatë përbashkëta/ui. Një komponent siprojekt-kartë, megjithatë, nuk duhet të jetë në të përbashkët, sepse i përket domenit të projekteve.
Gabimi i zakonshëm: vendosja e gjithçkaje në Shared
Një nga anti-modelet më të shpeshta është krijimi i një dosjejetë përbashkëtai madh që përmban gjithçka. Duket i përshtatshëm në fillim, por pas disa muajsh bëhet e pamundur të kuptosh se cilët komponentë janë vërtet gjenerikë dhe cilët janë vendosur atje vetëm për lehtësi.
Rregulli i përgjithshëm është i thjeshtë: nëse një komponent përmban fjalë, logjikë ose koncepte që lidhen me një veçori specifike, ai nuk ndahet. Nëse, megjithatë, është i përgjithshëm, i ripërdorshëm dhe i pavarur nga domeni, ai mund të shkojë në shared.
Komponentët e pavarur: Modern Angular pa NgModule të panevojshme
Me Angular moderne, ikomponentë të pavarurato janë bërë mënyra e rekomanduar për të ndërtuar aplikacione më të thjeshta, më eksplicite dhe modulare. Në të kaluarën, çdo komponent duhej të deklarohej brenda aNgModuli. Kjo shpesh çoi në krijimin e formave shumë të mëdha ose të paqarta.
Me komponentë të pavarur, çdo komponent deklaron drejtpërdrejt varësitë e tij përmes pronësimportet. Kjo e bën kodin më të lexueshëm: duke parë një komponent ju mund të kuptoni menjëherë se cilët komponentë të tjerë, direktiva ose tubacione përdor.
@Component({
selector: 'app-project-card',
standalone: true,
imports: [DatePipe],
template: `
<article class="project-card">
<h3>{{ project().title }}</h3>
<p>{{ project().description }}</p>
<small>Pubblicato il {{ project().createdAt | date }}</small>
<button type="button" (click)="open.emit(project().id)">
Apri progetto
</button>
</article>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProjectCardComponent {
project = input.required<Project>();
open = output<string>();
}
Kjo qasje ka disa përparësi:
- varësitë janë të qarta;
- komponentët janë më të lehtë për t'u lëvizur dhe testuar;
- ngarkimi dembel bëhet më i natyrshëm;
- nevoja për module të ndërmjetme është zvogëluar;
- projekti ndihet më i lehtë mendërisht.
Bootstrap i aplikacionit me të pavarur
Në një aplikacion modern Angular, bootstrapping mund të trajtohet pa aAppModuletradicionale. Konfigurimi global shpesh përcaktohet nëapp.config.ts.
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes, withComponentInputBinding()),
provideHttpClient(withInterceptors([
authInterceptor,
apiErrorInterceptor
]))
]
};
Dhe në dosjekryesore.ts:
bootstrapApplication(AppComponent, appConfig)
.catch((error) => console.error(error));
Ky konfigurim ndan qartë pikën e nisjes së aplikacionit nga logjika e veçorive.
Drejtimi dhe ngarkimi dembel për veçoritë
Routing është një pjesë qendrore e arkitekturës Angular. Në një projekt të shkallëzuar, çdo veçori duhet të ketë rrugët e veta, të ngarkuara me dembel sa herë që është e mundur.
Në dosjen kryesoreapp.rrugët.tsne mund të përcaktojmë vetëm rrugët kryesore:
export const routes: Routes = [
{
path: '',
loadComponent: () =>
import('./features/home/pages/home-page.component')
.then((m) => m.HomePageComponent)
},
{
path: 'projects',
loadChildren: () =>
import('./features/projects/projects.routes')
.then((m) => m.PROJECTS_ROUTES)
},
{
path: 'blog',
loadChildren: () =>
import('./features/blog/blog.routes')
.then((m) => m.BLOG_ROUTES)
},
{
path: 'admin',
canMatch: [authGuard],
loadChildren: () =>
import('./features/admin/admin.routes')
.then((m) => m.ADMIN_ROUTES)
}
];
Brenda funksionitprojektetnë vend të kësaj, ne përcaktojmë rrugët specifike:
export const PROJECTS_ROUTES: Routes = [
{
path: '',
loadComponent: () =>
import('./pages/projects-list-page.component')
.then((m) => m.ProjectsListPageComponent)
},
{
path: ':id',
loadComponent: () =>
import('./pages/project-detail-page.component')
.then((m) => m.ProjectDetailPageComponent)
}
];
Kjo qasje e mban të pastër skedarin e rrugëve kryesore dhe lejon Angular të ngarkojë vetëm kodin e nevojshëm për seksionin e vizituar nga përdoruesi.
Sinjalet: menaxhim më i thjeshtë dhe më i përgjegjshëm i shtetit
THEsinjaletato janë një nga mjetet më të rëndësishme në Angular modern. Ato ju lejojnë të menaxhoni gjendjen lokale dhe të prejardhur në një mënyrë më të thjeshtë, më të lexueshme dhe më efikase se shumë zgjidhje tradicionale.
Një sinjal përfaqëson një vlerë reaktive. Kur vlera ndryshon, Angular e di se cilat pjesë të UI duhet të përditësohen. Kjo e bën shtetin më të qartë dhe redukton kompleksitetin në shumë skenarë.
Shembull i thjeshtë:
const count = signal(0);
const double = computed(() => count() * 2);
function increment(): void {
count.update((value) => value + 1);
}
Në një aplikacion të shkallëzuar, sinjalet mund të përdoren në nivele të ndryshme:
- gjendja lokale e komponentit, të tilla si skeda aktive, filtra, hapja e një modali;
- gjendje e prejardhur, të tilla si elementet totale, elementet e filtruar, gjendja boshe;
- dyqan funksionesh, kur një seksion i aplikacionit ka të dhëna të ndara midis disa komponentëve;
- fasada, për të ekspozuar një gjendje të thjeshtë dhe të gatshme në UI.
Shembull i dyqanit me sinjale
Një praktikë e mirë është krijimi i një dyqani specifik për veçorinë, duke shmangur vendosjen e të gjithë logjikës brenda komponentëve.
@Injectable()
export class ProjectsStore {
private readonly api = inject(ProjectsApiService);
private readonly _projects = signal<Project[]>([]);
private readonly _loading = signal(false);
private readonly _error = signal<string | null>(null);
private readonly _query = signal('');
readonly projects = this._projects.asReadonly();
readonly loading = this._loading.asReadonly();
readonly error = this._error.asReadonly();
readonly query = this._query.asReadonly();
readonly filteredProjects = computed(() => {
const query = this._query().toLowerCase().trim();
if (!query) {
return this._projects();
}
return this._projects().filter((project) =>
project.title.toLowerCase().includes(query)
);
});
readonly total = computed(() => this.filteredProjects().length);
async loadProjects(): Promise<void> {
this._loading.set(true);
this._error.set(null);
try {
const projects = await firstValueFrom(this.api.getProjects());
this._projects.set(projects);
} catch {
this._error.set('Impossibile caricare i progetti.');
} finally {
this._loading.set(false);
}
}
setQuery(query: string): void {
this._query.set(query);
}
}
Ky dyqan ka një përgjegjësi të qartë: menaxhoni statusin e veçorisë së projektit. Komponenti nuk ka nevojë të dijë se si ngarkohen të dhënat ose si filtrohen. Thjesht duhet të lexojë sinjalet dhe metodat e thirrjeve të ekspozuara nga dyqani.
Ku të sigurohet një dyqan funksionesh
Nëse dyqani shërben vetëm për një veçori specifike, ai mund të ofrohet në nivelin e itinerarit ose komponentit. Në këtë mënyrë cikli i tij jetësor lidhet me vetë veçorinë dhe nuk mbetet global pa nevojë.
export const PROJECTS_ROUTES: Routes = [
{
path: '',
providers: [ProjectsStore, ProjectsApiService],
loadComponent: () =>
import('./pages/projects-list-page.component')
.then((m) => m.ProjectsListPageComponent)
}
];
Kjo zgjedhje shmang mbushjen e injektorit rrënjë me shërbime që nuk kanë nevojë të jetojnë gjatë gjithë jetës së aplikacionit.
Sinjalet dhe RxJS: ata nuk janë armiq
Një gabim i zakonshëm është të mendosh se sinjalet zëvendësojnë plotësisht RxJS. Në realitet, të dy mjetet mund të bashkëjetojnë shumë mirë. Sinjalet janë të shkëlqyera për gjendjen sinkrone, derivacionet dhe gjendjen UI. RxJS mbetet shumë i dobishëm për transmetime asinkrone, ngjarje komplekse, WebSockets, debouncing, riprovim, kombinime transmetimi dhe anulim automatik të kërkesave.
Një rregull praktik i dobishëm:
- SHBAsinjaletpër të përfaqësuar gjendjen aktuale të UI;
- SHBAi llogariturpër vlerat e prejardhura;
- SHBARxJSkur ju duhet të modeloni rrjedhat asinkrone me kalimin e kohës;
- përdorni konvertime si
te Sinjalikur dëshironi t'i ekspozoni shabllonit një Observable në një mënyrë më të thjeshtë.
Komponentët inteligjentë kundër memecë
Modelikomponentë inteligjentë kundër memecëështë një nga më të dobishmet për të mbajtur të pastër një aplikacion Angular. Ideja është që të veçohen komponentët që trajtojnë logjikën, të dhënat dhe komunikimin nga ata që merren vetëm me shfaqjen e informacionit.
Komponentët inteligjentë
Komponentët inteligjentë, të quajtur edhe komponentë kontejnerë, janë komponentë të vetëdijshëm për veçoritë. Ata mund të përdorin shërbimet, dyqanet, ruterat, parametrat e rrugës dhe logjikën e aplikacionit. Ato zakonisht korrespondojnë me një faqe ose një komponent kryesor të veçorisë.
Shembull i komponentit inteligjent:
@Component({
selector: 'app-projects-list-page',
standalone: true,
imports: [
ProjectCardComponent,
ProjectFiltersComponent,
SpinnerComponent
],
template: `
<section>
<h2>Progetti</h2>
<app-project-filters
[query]="store.query()"
(queryChange)="store.setQuery($event)"
/>
@if (store.loading()) {
<app-spinner />
} @else if (store.error()) {
<p class="error">{{ store.error() }}</p>
} @else {
<div class="projects-grid">
@for (project of store.filteredProjects(); track project.id) {
<app-project-card
[project]="project"
(open)="openProject($event)"
/>
}
</div>
}
</section>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProjectsListPageComponent implements OnInit {
readonly store = inject(ProjectsStore);
private readonly router = inject(Router);
ngOnInit(): void {
this.store.loadProjects();
}
openProject(id: string): void {
this.router.navigate(['/projects', id]);
}
}
Ky komponent është i zgjuar sepse koordinon faqen: ngarkon të dhënat, lexon dyqanin, menaxhon navigimin dhe kalon informacion tek komponentët fëmijë.
Komponentët memecë
Komponentët memecë, të quajtur edhe komponentë prezantues, janë komponentë të thjeshtë, të ripërdorshëm dhe të lehtë për t'u testuar. Ata marrin të dhëna nëpërmjet hyrjes dhe emetojnë ngjarje nëpërmjet daljes. Ata nuk duhet të njohin ruterat, shërbimet HTTP apo dyqanet globale.
@Component({
selector: 'app-project-filters',
standalone: true,
template: `
<label>
Cerca progetto
<input
type="search"
[value]="query()"
(input)="onInput($event)"
placeholder="Cerca per titolo..."
/>
</label>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProjectFiltersComponent {
query = input('');
queryChange = output<string>();
onInput(event: Event): void {
const target = event.target as HTMLInputElement;
this.queryChange.emit(target.value);
}
}
Ky komponent nuk di asgjë për funksionin e plotë. Nuk e di se nga vijnë projektet, nuk e njeh API-në, nuk navigon, nuk e modifikon drejtpërdrejt dyqanin. Detyra e tij është vetëm të tregojë një hyrje dhe të komunikojë vlerën e re.
Sepse kjo ndarje funksionon
Ndarja e komponentëve të zgjuar dhe memecë sjell përparësi konkrete:
- komponentët prezantues janë më të lehtë për t'u ripërdorur;
- testet bëhen më të lehta;
- logjika e aplikimit mbetet në kontejnerë ose dyqane;
- UI bëhet më e parashikueshme;
- zvogëlohet rreziku i komponentëve të mëdhenj e të vështirë për t'u mirëmbajtur.
Megjithatë, ne nuk duhet ta zbatojmë modelin në mënyrë dogmatike. Për komponentë shumë të vegjël ose veçori të thjeshta, një komponent i vetëm mund të jetë i mjaftueshëm. Qëllimi nuk është krijimi i sa më shumë skedarëve, por ruajtja e përgjegjësive të qarta.
Struktura e rekomanduar e dosjeve për një aplikacion Angular të shkallëzuar
Një strukturë solide për një aplikacion modern Angular mund të duket kështu:
src/
app/
app.component.ts
app.config.ts
app.routes.ts
core/
auth/
auth.service.ts
auth.guard.ts
auth.interceptor.ts
http/
api-error.interceptor.ts
layout/
main-layout.component.ts
admin-layout.component.ts
services/
logger.service.ts
storage.service.ts
shared/
ui/
button/
button.component.ts
modal/
modal.component.ts
spinner/
spinner.component.ts
pipes/
truncate.pipe.ts
directives/
autofocus.directive.ts
models/
pagination.model.ts
api-response.model.ts
utils/
date.util.ts
features/
home/
pages/
home-page.component.ts
projects/
projects.routes.ts
pages/
projects-list-page.component.ts
project-detail-page.component.ts
components/
project-card.component.ts
project-filters.component.ts
data-access/
projects-api.service.ts
projects-store.service.ts
models/
project.model.ts
utils/
project-status.util.ts
blog/
blog.routes.ts
pages/
blog-list-page.component.ts
blog-detail-page.component.ts
components/
blog-card.component.ts
blog-search.component.ts
data-access/
blog-api.service.ts
blog-store.service.ts
models/
blog-post.model.ts
admin/
admin.routes.ts
pages/
admin-dashboard-page.component.ts
edit-post-page.component.ts
components/
admin-sidebar.component.ts
content-editor.component.ts
data-access/
admin-api.service.ts
admin-store.service.ts
models/
admin-user.model.ts
environments/
environment.ts
Kjo strukturë është mjaft e thjeshtë për t'u kuptuar shpejt, por mjaft e fortë për t'u rritur me kalimin e kohës.
Shpjegimi i dosjeve kryesore
app.config.tspërmban ofrues globalë: ruterë, klientë HTTP, interceptorë, konfigurime globale dhe shërbime aplikacioni.
app.rrugët.tspërmban vetëm rrugët kryesore dhe delegon itineraret e detajuara në veçoritë individuale.
bërthamëpërmban atë që jeton globalisht: auth, interceptorët, paraqitjet, regjistruesit, konfigurimet dhe shërbimet e infrastrukturës.
të përbashkëtapërmban elemente gjenerike dhe të ripërdorshme, pa logjikë specifike biznesi.
veçoritëpërmban zemrën e aplikacionit, të organizuar sipas domenit funksional.
faqetpërmban komponentë të lidhur drejtpërdrejt me rrugët. Ata janë zakonisht komponentë inteligjentë.
komponentëtpërmban komponentë specifikë të veçorive, shpesh memec ose gjysmë prezantues.
qasje në të dhënapërmban shërbime API, dyqan, fasadë dhe logjikë të aksesit të të dhënave.
modelepërmban ndërfaqe dhe lloje TypeScript që lidhen me veçorinë.
shërbimet komunalepërmban funksione të pastra specifike për veçori.
Shtresa e aksesit të të dhënave: izoloni API-në dhe gjendjen
Në një aplikacion Angular të shkallëzuar, komponentët nuk duhet të flasin drejtpërdrejt me tëHttpClient. Nëse secili komponent bën thirrje autonome HTTP, logjika dyfishohet dhe bëhet e vështirë për të menaxhuar ngarkimin, gabimet, caching dhe transformimin e të dhënave.
Më mirë të krijoni një nivelqasje në të dhënapër çdo veçori. Ky nivel mund të përmbajë:
- Shërbimet API;
- dyqane të bazuara në sinjale;
- fasadë;
- hartues midis modeleve DTO dhe UI;
- logjika e memorizimit lokal të veçorisë.
Shembull i shërbimit API:
@Injectable()
export class ProjectsApiService {
private readonly http = inject(HttpClient);
private readonly baseUrl = '/api/projects';
getProjects(): Observable<Project[]> {
return this.http.get<Project[]>(this.baseUrl);
}
getProjectById(id: string): Observable<Project> {
return this.http.get<Project>(`${this.baseUrl}/${id}`);
}
}
Komponenti nuk ka nevojë të dijë pikat fundore, URL-të ose detajet HTTP. Duhet të komunikojë me dyqanin ose me fasadë. Kjo e mban ndërfaqen të pastër dhe e bën testimin më të lehtë.
Rregullat kryesore për varësinë
Për të shmangur kaosin arkitektonik, është e dobishme të vendosni rregulla të qarta për varësitë:
- Një veçori mund të importohet nga e përbashkëta, sepse shared përmban elemente gjenerike.
- Një veçori mund të përdorë shërbimet bazë, si auth ose logger, nëse vërtet nevojitet.
- Shared nuk ka pse të importojë veçori, përndryshe pushon së qeni gjenerik.
- Shared duhet të shmangë varësitë e forta në thelbin, për të mbetur i ripërdorshëm.
- Një veçori nuk duhet të importojë drejtpërdrejt një veçori tjetër.
- Core nuk duhet të përmbajë logjikë specifike për një veçori të vetme.
Një drejtim i mundshëm i varësive është ky:
features ---> shared
features ---> core
core ---> shared
shared ---> nessuna feature
Sa më shumë të respektohen këto rregulla, aq më modular mbetet aplikacioni.
Konventat e rekomanduara të emërtimit
Konventat e emërtimit duken si detaje, por në projektet e mëdha ato bëjnë një ndryshim të madh. Një emër i qëndrueshëm redukton kohën që duhet për të kuptuar rolin e një skedari.
*.faqe.tspër komponentët e lidhur me një rrugë.*.komponent.tspër komponentët normalë të UI.*.shërbim.tspër shërbime të përgjithshme.*.api.service.tspër shërbimet që komunikojnë me backend.*.dyqan.tsose*.dyqan.shërbim.tssipas statusit të veçorive.*.roje.tspër roje të rrugës.*.përgjues.tspër interceptorët HTTP.*.model.tspër ndërfaqet dhe llojet kryesore.*.përdorim.tspër funksione të pastra mbështetëse.
Për shembull,projekt-detaje-faqe.komponent.tskomunikoni menjëherë se është një faqe.projekte-api.service.tskomunikon se ai shërbim flet me backend-in.projekte-dyqan.shërbim.tskomunikon se ai skedar menaxhon gjendjen e veçorisë.
Performanca dhe arkitektura
Një arkitekturë e mirë Angular nuk ka të bëjë vetëm me renditjen e skedarëve. Ajo gjithashtu ndikon në performancën e aplikacionit.
Përdorimi i veçorive të ngarkuara me dembel do të thotë të shmangni ngarkimin e të gjithë kodit në fillim. Nëse një përdorues viziton vetëm faqen kryesore, nuk ka kuptim që menjëherë të shkarkojë gjithashtu kodin për administratorin, redaktorin, pultin dhe të gjitha seksionet e brendshme.
Komponentët e pavarur ndihmojnë sepse e bëjnë më të lehtë ngarkimin e komponentëve dhe rrugëve në mënyrë të grimcuar. Sinjalet ndihmojnë sepse e bëjnë paraqitjen më të saktë dhe gjendjen më të lehtë për t'u gjurmuar. Modeli i zgjuar/memec ndihmon sepse redukton komponentë të mëdhenj dhe shabllone të vështirë për t'u optimizuar.
Disa praktika të mira:
- përdorni ngarkimin dembel për veçori që nuk nevojiten menjëherë;
- SHBA
ChangeDetectionStrategy.OnPushnë komponentët; - SHBA
@përmeudhëpër kryerjen e listave; - mbajini komponentët të vegjël dhe të fokusuar;
- shmang logjikën e rëndë direkt në shabllon;
- përdorin sinjale dhe llogaritur për vlerat e prejardhura;
- ngarkoni imazhe dhe asete në një mënyrë të optimizuar;
- ndani administratorin nga ballina publike sa herë që është e mundur.
Gabimet e zakonshme që duhen shmangur
1. Komponentët shumë të mëdhenj
Një komponent që përmban shabllone të mëdhenj, thirrje HTTP, trajtimin e formularëve, gjendjen, vërtetimet, hartën e të dhënave dhe logjikën e navigimit bëhet shpejt i pamenaxhueshëm. Nëse një anëtar tejkalon shumë përgjegjësi, është koha për t'i ndarë ato.
2. Shërbimet Globale për gjithçka
Jo të gjitha shërbimet duhet të jenëofrohetNë: 'rrënja'. Nëse një shërbim shërben vetëm për një veçori, ofrimi i tij në nivelin e itinerarit ose komponentit mund të jetë më i përshtatshëm. Kjo redukton gjendjen e panevojshme globale dhe e bën ciklin e jetës më të parashikueshëm.
3. E ndarë shumë e madhe
Një dosje e madhe e përbashkët është shpesh një shenjë e arkitekturës së dobët. Shared duhet të përmbajë komponentë dhe shërbime vërtet të përgjithshme, jo pjesë të veçorive të hedhura për lehtësi.
4. Veçoritë e bashkuara së bashku
Kur një veçori importon drejtpërdrejt skedarë nga një veçori tjetër, projekti bëhet i brishtë. Më mirë të tërhiqni kodin e përbashkët në një zonë të përbashkët me përgjegjësi të qartë.
5. Logjika e biznesit në shabllon
Modeli duhet të mbetet i lexueshëm. Nëse një kusht bëhet shumë i ndërlikuar, zhvendoseni atë në një metodë të llogaritur, të qartë ose në një dyqan funksionesh.
6. Mungesa e konventave
Nëse secili zhvillues krijon dosje dhe emra në mënyrën e tij, projekti humbet koherencën. Marrëveshjet duhet të jenë të thjeshta, të dokumentuara dhe të respektuara.
Shembull praktik: një veçori e mirëstrukturuar e Blogut
Le të imagjinojmë një seksion blogu me listën e artikujve, detajet e artikujve, kërkimin, filtrat e kategorive dhe menaxhimin e përmbajtjes në anën e administratorit. Një strukturë e porositur mund të jetë:
src/app/features/blog/
blog.routes.ts
pages/
blog-list-page.component.ts
blog-detail-page.component.ts
components/
blog-card.component.ts
blog-search.component.ts
blog-category-filter.component.ts
blog-empty-state.component.ts
data-access/
blog-api.service.ts
blog-store.service.ts
models/
blog-post.model.ts
blog-category.model.ts
utils/
reading-time.util.ts
slug.util.ts
Faqjablog-listë-faqeështë e zgjuar: përdorni dyqanin, ngarkoni të dhënat, menaxhoni filtrat dhe kërkoni. Komponentikartë bloguështë memece: merr një artikull dhe tregon titullin, fragmentin, imazhin dhe datën. Shërbiminblog-apibisedoni me backend. Dyqani ruan statusin, ngarkimin, gabimet dhe artikujt e filtruar.
Ky lloj ndarjeje ju lejon të ndryshoni paraqitjen e kartës pa prekur logjikën e të dhënave ose të ndryshoni pikat fundore të API pa ndryshuar komponentët e prezantimit.
Lista përfundimtare e kontrollit për një arkitekturë këndore të shkallëzuar
Përpara se të shqyrtoni strukturën e qëndrueshme të aplikacionit tuaj Angular, mund të përdorni këtë listë kontrolli:
- Karakteristikat kryesore janë të organizuara brenda
veçoritë? - A ka çdo veçori rrugë, faqe, komponentë, akses të të dhënave dhe modele të veçanta?
- Kodi global është me të vërtetë i kufizuar në
bërthamë? të përbashkëtaa përmban vetëm elementë gjenerikë dhe të ripërdorshëm?- A i shmangin veçoritë varësitë e drejtpërdrejta mes tyre?
- A përdorin rrugët kryesore ngarkim dembel?
- A i deklarojnë qartë përbërësit e pavarur varësitë e tyre?
- A është logjika HTTP jashtë komponentëve?
- A menaxhohet statusi i veçorisë nga dyqanet, fasadat apo shërbimet e dedikuara?
- A përdoren sinjalet për statusin lokal dhe vlerat e prejardhura?
- A përdoret RxJS aty ku vërtet duhet të modeloni rrjedhat asinkrone?
- A kanë përgjegjësi të ndara komponentët e zgjuar dhe memecë?
- A janë emrat e skedarëve të qëndrueshëm?
- A janë komponentët të vegjël, të lexueshëm dhe të testueshëm?
- A ka një konventë të përbashkët nga ekipi?
konkluzioni
Të organizosh mirë një projekt Angular nuk do të thotë të krijosh një strukturë të komplikuar. Do të thotë krijimi i një strukture që është e qartë, e parashikueshme dhe e përshtatshme për rritje. Arkitektura e bazuar në veçori ju ndihmon të mendoni për funksionalitetin e botës reale. Ndarja midis bërthamës dhe të përbashkët shmang konfuzionin. Komponentët e pavarur i bëjnë varësitë më të qarta. Sinjalet thjeshtojnë menaxhimin e shtetit. Modeli i komponentëve inteligjentë dhe memecë përmirëson lexueshmërinë, ripërdorimin dhe testueshmërinë.
Gjëja më e rëndësishme është të mos prisni derisa projekti të bëhet i madh për të menduar për arkitekturën. Vendimet e marra në fillim ndikojnë në të gjithë ciklin jetësor të aplikacionit. Një strukturë e pastër ju lejon të shtoni faqe të reja, veçori të reja dhe zhvillues të rinj pa e kthyer kodin në një labirint.
Nëse po ndërtoni një aplikacion profesional Angular, filloni nga një rregull i thjeshtë: gjithçka duhet të ketë një vend specifik. Veçoritë duhet të përmbajnë logjikën e produktit, thelbi duhet të përmbajë infrastrukturën globale, të përbashkëta duhet të përmbajë elementë vërtet të ripërdorshëm. Nga atje, shtoni komponentë të pavarur, ngarkim dembel, sinjale dhe një ndarje të qartë midis komponentëve të zgjuar dhe memecë.
Një arkitekturë e shkallëzuar nuk është arkitektura më komplekse, por ajo që ekipi mund ta kuptojë, ruajë dhe evoluojë me kalimin e kohës. Angular ofron të gjitha mjetet e nevojshme: na takon ne t'i përdorim ato me disiplinë, qëndrueshmëri dhe sens të përbashkët.