Идея этого примера состоит в том, чтобы реализовать архитектуру сервиса и репозитория, в которой репозиторий имеет несколько реализаций, прозрачных для сервиса, и все это с использованием структуры внедрения зависимостей и инверсии, предложенной Angular, позволяющей сохранять уровни отдельно от нашего приложения с помощью доменно-ориентированный дизайн.

Слой домена

export default abstract class AuthRepository {
  abstract login(email: string, password: string): Promise<boolean>;
}

Прикладной уровень

import AuthRepository from '../domain/authRepository';

export default class AuthService {
  constructor(readonly repository: AuthRepository) {
    this.repository = repository;
  }

  async login(email: string, password: string) {
    const isLogged = await this.repository.login(email, password);
    return isLogged;
  }
}

Уровень архитектуры

import AuthRepository from '../domain/authRepository';

export default class AuthInMemoryRepository extends AuthRepository {
  private users = [{ email: '[email protected]', password: '123456' }];

  async login(email: string, password: string): Promise<boolean> {
    const matchUser = this.users.find(
      (u) => u.email === email && u.password === password
    );
    return matchUser !== undefined;
  }
}

Следующим шагом будет использование Angular injectToken для инверсии зависимостей и построения сервиса, избегая реализации репозитория.

./tokens.ts

import { InjectionToken } from '@angular/core';
import AuthRepository from '../domain/authRepository';

export const AUTH_REPOSITORY_TOKEN = new InjectionToken<AuthRepository>(
  'AuthRepository'
);

Последним шагом настройки будет указание Angular, как создавать наши экземпляры и какие классы использовать при инверсии зависимостей.

./app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import AuthService from 'src/core/application/authService';
import AuthInMemoryRepository from 'src/core/architecture/authInMemoryRepository';
import { AUTH_REPOSITORY_TOKEN } from 'src/core/architecture/tokens';
import AuthRepository from 'src/core/domain/authRepository';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, AppRoutingModule, FormsModule, ReactiveFormsModule],
  providers: [
    {
      provide: AUTH_REPOSITORY_TOKEN,
      useClass: AuthInMemoryRepository,
    },
    {
      provide: AuthService,
      useFactory: (authRepository: AuthRepository) => {
        return new AuthService(authRepository);
      },
      deps: [AUTH_REPOSITORY_TOKEN],
    },
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

В завершение мы просто импортируем сервис в наш компонент, а angular automanticante внедряет реализацию репозитория.

./app.comComponent.ts

import { Component } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import AuthService from 'src/core/application/authService';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  constructor(private service: AuthService) {}
  public form = new FormGroup({
    email: new FormControl('', [Validators.required, Validators.email]),
    password: new FormControl('', [Validators.required]),
  });
  async onSubmit() {
    const { email, password } = this.form.value;
    if (email && password) {
      const isLogged = await this.service.login(email, password);
      alert(isLogged ? 'Logged' : 'Not logged');
    }
  }
}

Pero compartir no es amoral – es un emperativo моральный. Только то, что было сказано, чтобы код был неверен, и ему пришлось сделать копию с другом.

- Аарон Шварц