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

3
我们目前正在开发一个Angular4应用程序,并希望获得关于服务架构的反馈。
该应用程序的五个“模块”分别是:
- one - two - three - four - five 目前,我们有一个仅针对one的数据服务。但是,抽象类ApiService可以在其他四个模块中导入(请参见下面的代码)。
以下是我考虑的一些选项:
选项1: 将抽象类ApiService移动到我们的共享文件夹模块中,即将共享文件夹模块导入到每个模块中。然后创建一个针对每个模块特定的服务,该服务继承自ApiService。这样可以轻松管理每个单独的服务。
选项2: 将抽象类移动到共享文件夹中,并创建一个包含所有五个模块的API调用的全局服务。这样,我们就有了一个单一的服务来管理所有API连接。然而,文件可能会变得有点大并且难以管理。您对如何组织有何想法?
选项3: 完全放弃可观察到的服务,并使用类似ngrx/store来处理状态。
我正在寻求有关数据服务架构的反馈。
module-one-data-service.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是抽象的,因此无需将其添加到模块的提供程序数组中。Ngrx是一个不错的选择,但仍然需要在服务中保留您的http调用,只需从Effects中调用即可。 - Matt Sugden
感谢@MattSugden。选项#3是一种选择,但对我们的团队来说最具挑战性,因为我们中没有人有Ngrx经验。我理解这个概念,但需要时间来适应。我想知道是否更好地采用全局服务路线(1个服务)还是为每个模块专门提供服务。如果有更好的工具,例如Ngrx,是否值得放弃现有的内容,并只使用Ngrx来处理状态? - Blexy
去Ngrx的GitHub页面,他们有一个使用所有最佳实践的示例应用程序,这是一个很好的起点。 - Matt Sugden
1个回答

1

ApiService 应该不需要是抽象类。根据您发布的内容,它实际上是一个包装器,用于管理 Angular HTTP 服务。由于 Angular HTTP 服务具有糟糕的 API,因此经常需要封装它。

需要访问封装的 HTTP 功能的服务类应该使用 注入 这个 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.
}

使用这种方法,唯一共享的可注入项是 ApiServiceFactory。您可以在共享模块中提供它,也可以在每个注入它的服务所在的各个模块中独立提供它。由于服务是无状态的,而工厂返回的实际对象是有效地瞬态的,因此您不必担心额外的实例或任何类似的问题。
请注意,如果Angular提供了对这种模式的内置支持,那将会很好,因为传递式注入相当普遍并且具有许多用例。虽然目前可以在组件级别上实现瞬态依赖项注入行为,但在服务级别上没有办法做到这一点,除非创建这样的工厂。
相比之下,像Aurelia这样的框架允许将服务装饰为简单的 @transient,并以类似简单的方式显式请求一个新实例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接