我们目前正在开发 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/