Angular4 基于模块的服务与全局服务

标签 angular architecture angular2-services

我们目前正在开发 Angular4 应用程序,并正在寻找有关服务架构的反馈。

应用程序的五个“模块”是:

  • 一个
  • 两个
  • 三个
  • 四个
  • 五个

目前我们有一个特定于one 的数据服务,但是,抽象类ApiService 可以跨其他四个模块导入(请参见下面的代码)。

以下是我的一些想法:

选项 1: 将抽象类 ApiService 移动到我们的共享文件夹模块中,即共享文件夹模块被导入到五个模块中的每一个中。

然后创建一个特定于从 ApiService 继承的每个模块的服务。这将使管理每个单独的服务变得容易。

选项 2: 将抽象类移动到我们的共享文件夹中,并创建一个全局服务,其中包含所有五个模块的所有 api 调用。这样我们就有了一个单一的服务来管理所有的 API 连接。但是,该文件可能会变得有点大并且难以管理。关于组织的想法?

选项 3: 将可观察的服务全部废弃,并使用 ngrx/store 之类的东西来处理状态。

我正在寻找有关数据服务架构的反馈。

模块一数据服务.ts

import { Injectable } from '@angular/core';
import { Http, Response, Headers, RequestOptions } from '@angular/http';

import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';

import { IPosition } from './position.model';
import { IPositionReference } from '../shared/side-pane/position-reference.model';
import { Subject } from 'rxjs/Subject';


export abstract class ApiService {
  protected BASE_URL = 'http://justtheurl.com';
  protected baseAndModuleUrl: string;

  private OPTIONS = new RequestOptions({
      headers: new Headers({
        'Authorization' : 'Basic thisIsJustForTesting'
      }),
      withCredentials: true
  });

  constructor(private http: Http, private module: string) {
    this.baseAndModuleUrl = `${this.BASE_URL}${module}`;
  }

  public getBaseModuleUrl() { return this.baseAndModuleUrl; }

  protected fetch(apiAction: string): Observable<any> {
    return this.http
      .get(`${this.baseAndModuleUrl}${apiAction}`, this.OPTIONS)
      .map((res: Response) => res.json().data);
  }

  protected post(apiAction: string, positions: Object[]): Observable<any> {
    return this.http
      .post(`${this.baseAndModuleUrl}${apiAction}`, positions, this.OPTIONS)
      .map((res: Response) => res.json());
  }

  protected upload(apiAction: string, file: FormData): Observable<any> {
    return this.http
      .post(`${this.baseAndModuleUrl}${apiAction}`, file, this.OPTIONS)
      .map((res: Response) => res.json());
  }

}

@Injectable()
export class ModuleOneDataService extends ApiService {
  public editableValues = new Subject<any>();

  constructor(http: Http) { super(http, '/api/module/one'); }

  public fetchTree(): Observable<IPosition[]> { return this.fetch('/tree'); }

  public fetchList(): Observable<IPosition[]> { return this.fetch('/list'); }

  public fetchIndividual(id: string): Observable<IPositionReference> { return this.fetch(`/node/${id}`); }

  public savePositionsToSubgraph(positions: Object[]): Observable<any> { return this.post('/subgraph/upsert', positions); }

  public mergeSubraphToMaster(): Observable<Object> { return this.post('/subgraph/merge', [{}]); }


}

最佳答案

ApiService 可能根本不应该是抽象的。查看您发布的内容,它实际上充当管理 Angular HTTP 服务的包装器。经常需要包装 Angular HTTP 服务,因为它有如此糟糕的 API。

需要访问包装的 HTTP 设施的服务类应该注入(inject)这个 API 服务而不是从它继承。

原因是它们不是基类的逻辑后代,仅仅为了代码共享而使用继承会导致代码库困惑。有更好的方法。

这是我的推荐

app/module-one/data-service.ts

import {Injectable} from '@angular/core';
import {Observable} from 'rxjs/Observable';

import ApiServiceFactory, {ApiService} from 'app/shared/api';

@Injectable() export class DataService {
  constructor(apiServiceFactory: ApiServiceFactory) {
    this.api = apiServiceFactory.create('/api/module/one');
  }

  api: ApiService;

  fetchTree(): Observable<IPosition[]> { 
    return this.api.fetch('/tree');
  }

  fetchList(): Observable<IPosition[]> {
    return this.api.fetch('/list');
  }

  fetchIndividual(id: string): Observable<IPositionReference> {
    return this.api.fetch(`/node/${id}`);
  }
}

app/shared/api.ts

import {Injectable} from '@angular/core';
import {Http} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Injectable() export default class ApiServiceFactory {
  constructor(readonly http: Http) {}

  create(moduleApiSubpath: string): ApiService {
    return new ApiServiceImplementation(this.http, moduleApiSubpath);
  }
}

export interface ApiService {
  fetch(url): Observable<{}>;

  post(url:string, body: {}): Observable<{}>;

  // etc.
}

const baseUrl = 'http://justtheurl.com';

// there's actually no need to make this a class at all.
// It could be as simple as a function that returns an object literal.
// It doesn't matter much since it's not exported.
class ApiServiceImplementation implements ApiService {
  constructor(readonly http: Http, readonly moduleApiSubpath: string){}

  get baseModuleUrl() {
    return `${baseUrl}${this.moduleApiSubpath}`;
  }

  fetch(apiAction: string): Observable<{}> {
    return this.http
      .get(`${this.baseModuleUrl}${apiAction}`, this.options)
      .map(res => res.json().data);
  }

  // etc.
}

使用这种方法,唯一的共享注入(inject)将是 ApiServiceFactory。您可以在共享模块中提供它,也可以在每个具有注入(inject)它的服务的模块中独立提供它。您不必担心额外的实例或任何类似的问题,因为该服务是无状态的,并且工厂返回的实际对象实际上是 transient 的。

请注意,如果 Angular 为这种模式提供内置支持,那就太好了,因为传递注入(inject)相当普遍并且有很多用例。虽然目前可以在组件级别实现 transient 依赖注入(inject)行为,但如果不创建这样的工厂,就无法在服务级别实现。

相比之下,像 Aurelia 这样的框架允许使用简单的 @transient 装饰服务,并且消费者可以以类似的简单方式明确请求新实例。

关于Angular4 基于模块的服务与全局服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45091973/

相关文章:

javascript - Angular 6 - jsPDF -autotable - PDF 未正确生成

javascript - 如何在 Angular JS 中包含模块级 CSS

java - 用户登录状态机 - 存储和实现

Angular2 没有服务提供者 - 外部模块

Angular Http 不工作。 Promise 与 Observable 方法混合

javascript - 基于数组值切换 Angular 组件 CSS 类

angular - RXJS 5 .subscribe() 没有参数

entity-framework - 领域驱动设计和 Entity Framework 中的实体是否应该相同?

c# - 报告一百万行的最佳方法

angular - 将 Angular 2 node_modules 文件编译为一个文件