Hi im really questioning the usage of a "asObserveable()" call on subject.
In my view it creates a big unecassary overhead.
The prevention of calls like "next()" or "complete()" are in my view useless.
Can you name me a good reason why you should do this?
Just compare this two
Without
export class TestService {
public get test$(): Observable<test> {
return this.test$.asObservable();
}
public get test2$(): Observable<test> {
return this.test2$.asObservable();
}
public get test3$(): Observable<test3> {
return this.test3$.asObservable();
}
public get test4$(): Observable<test4> {
return this.test4$.asObservable();
}
private readonly _test1$ = new ReplaySubject<test1>(1);
private readonly _test2$ = new ReplaySubject<test2>(1);
private readonly _test3$ = new ReplaySubject<test3>(1);
private readonly _test4$ = new ReplaySubject<test4>(1);
}
Without
export class TestService {
public readonly test1$ = new ReplaySubject<test1>(1);
public readonly test2$ = new ReplaySubject<test2>(1);
public readonly test3$ = new ReplaySubject<test3>(1);
public readonly test4$ = new ReplaySubject<test4>(1);
}
Downsides of subject's asObservable
Some boiler-plate code. Not much else.
In my view it creates a big unnecessary overhead.
Have you measured it? Subjects extend Observables, all it does is create a shallow copy. I'd be surprised if you found an app where the difference is bigger than the variance (effectively not measurable).
Upsides of subject's asObservable
A cleaner architecture means it's easier to find bugs and/or easier to stop bugs from being created in the first place.
Encapsulation is one of the fundamentals of a clean architecture. Because encapsulation hides some part of our program from other parts, it makes each part a bit easier to reason about. Therefore it is easier to understand, write, extend, and maintain.
If there is a bug in a well architected system, the surface area is much smaller.
In general, the benefits for these sorts of decisions tend to make themselves known on larger projects with bigger teams. If you're writing a hobby project at home or making a minimal viable product by yourself or a small team, it might makes sense to forgo over-planning in order to make haste. Such a project might need a re-write/overhaul once it grows, but by then the added effort will be worth it.
Alternatives to Subject's asObservable
If you have a static type checker set relatively strictly (TypeScript, Elm, PureScript, ClojureScript, etc), then you can just return the Subject as an Observable without making any changes to the runtime representation of the type.
You get the encapsulation with zero run-time cost.
I can give a good reason. Suppose, you want to emit some event execCompleted$ based on certain action . Here is how it is implememted
export class TestService {
private readonly execCompleted$ = new Subject<string>();
public async makeServerCall(userData){
const resp = await this.http.post('...').toPromise();
this.execCompleted$.next(resp.data.id);
return resp;
}
public getUpdatedId(){
this.execCompleted$.asObservable();
}
}
Now, this service is used by two Components -> Admin and UserInfo.
The Admin component can update some user info by using makeServerCall() and update some user data. This event has to be captured by UserInfoComponent so that it can listen and update the info (once some update has been performed).
By exposing Subject using getUpdatedId() , you are restricting any other component to mistakingly send the next() event on execCompleted$. This Subject will only get triggered when makeServerCall(userData) is called.
This is just a simple example. There are more complex cases but all has the same underlying intention.
To not allow unwanted events to be emitted or cancelled. Only the intended source should so that.
This is a standard practice in programming where you want some restrictions on what can be extended by others and what can't. Kinda like open-closed principal.
Related
I am learning SOLID principles. I read a lot of things about the single responsibility principle but I don't get it exactly. I will give an example that what do I want to say.
Let's say we have an article service for managing articles. How should I design article service by single responsibility.
Is like that:
class ArticleService{
create(){};
read(){};
update(){};
delete(){};
}
Or create a class for each operations like that :
class ArticleCreateService{
create(){};
}
class ArticleReadService{
read(){};
}
// and so on ...
According to single responsibility which is the best way to managing articles?
Thanks.
As Robert C. Martin (Uncle Bob) says about Single Responsibility Principle:
There should never be more than one reason for a class to change
So your first option is better to comply with Single Responsibility principle:
class ArticleService {
create(){};
read(){};
update(){};
delete(){};
}
As when you will want to edit ArticleService, then there is just one reason to edit this class. And this reason is to edit Article.
The second version looks like Interface Segregation principle. However, it should be slightly modified.
class ArticleCreateService
{
void Create() { }
}
class ArticleReadService
{
void Read() { }
}
At first, we need to segregate methods of class. We can segregate by Create() and Read() methods.
So let's create these interfaces:
public interface IReadable
{
void Read();
}
public interface ICreatable
{
void Create();
}
And modified version of ArticleService could look like this:
public class ArticleService : IReadable, ICreatable
// public class ArticleService implements IReadable, ICreatable // in TypeScript
{
public void Read()
{
throw new NotImplementedException();
}
void void Create()
{
throw new NotImplementedException();
}
}
The S in solid stands for that a class or method should only have one reason to change. That means that a class, module or method should have a single well defined responsibility.
In this particular case you might want to (for whatever reason) extend read, or write etc to read and write from/to different sources for example. Therefore keeping those responsibilities in a class each, will make it easier to extend i.e:
read class -> only reads data -> this class can then be extended with more methods like readFromExcel or readFromDB. Reading is a single responsibility. In that class each method can have separate niches of that one responsibility i.e readFromExcel only has one responsibility i.e readingFromExcel only.
class Read {
readFromExcel();
readFromDB();
}
A good rule of thumb is: does my class have one single responsibility? and what is that responsibility? Can my method and classes be extended without them losing that one single responsibility? In the above example class read has (S)ingle responsibility of only reads data and within it method readFromDB(); only reads files from the database.
I'm working on a large CMS system where a particular module and its submodules take advantage of the same backend API. The endpoint is exactly the same for each submodule aside from its "document type".
So a pattern like this is followed:
api/path/v1/{document-type}
api/path/v1/{document-type}/{id}
api/path/v1/{document-type}/{id}/versions
As time goes on the number of modules that use this API grows and I am left with many, many redundant api services that implement 7 CRUD methods:
getAllXs() {...}
getX(id) {...}
getXVersion(id, versionId) {...}
etc...
with an individual method looking like this
getAllXs() {
let endpoint = BASE.URL + ENDPOINTS.X;
let config = ...
return http.get(endpoint, config)
.then(response => response.data);
.catch(...);
}
Where X would be the name of a particular Document Type.
I came to a point where I decided to make a single service and do something like this:
const BASE_URL = window.config.baseUrl + Const.API_ENDPOINT;
const ENDPOINTS = {
"W": "/v1/W/",
"X": "/v1/X/",
"Y": "/v1/Y/",
"Z": "/v1/Z/",
}
getAllDocuments(docType, config={}) {
let endpoint = BASE_URL + ENDPOINTS[docType];
return http.get(endpoint, config)
.then(response => response.data);
.catch(...);
}
...other methods
Where a type is specified and a mapped endpoint is used to build the path.
This reduces all of the document api services down to one. Now this is more concise code wise, but obviously now requires an extra parameter and the terminology is more generic:
getAllXs() --> getAllDocuments()
and it's a bit less 'idiot-proof'. What makes me insecure about the current way it is written is that there are 6 modules that use this API and the same 7 methods in each service.
The questions I keep asking myself are:
Am I bordering anti-pattern with the dynamic functions?
What if I had 10+ modules using the same API?
Your question made me think of a common Object Relational Mapping design problem.
There are no single source of truth when it comes to design, but if your recognize an ORM in what you are building and value object oriented design principles, I have some inspiration for you.
Here's an over simplification of my own vanilla ES6 ORM I have used on many projects (reusing your code snippets for relatability). This deisgn is inspired by heavy ORM frameworks I have used in other languages.
class ORM {
constructor() {
this.BASEURL = window.config.baseUrl + Const.API_ENDPOINT
this.config = {foo:bar} // default config
}
getAll() {
let endpoint = this.BASEURL + this.ENDPOINT
return http.get(endpoint, this.config)
.then(response => response.data)
.catch(...)
}
get(id) {
// ...
}
}
And examples of extension of that class (notice the one with a special configuration)
class XDocuments extends ORM {
static endpoint = '/XDocument/'
constuctor() {
super()
}
otherMethod() {
return 123
}
}
class YDocuments extends ORM {
static endpoint = '/YDocument/'
constuctor() {
super()
}
getAll() {
this.config = {foo:not_bar}
super.getAll()
}
}
Since you are specifically asking if this is bordering anti-patterns. I would suggest reading about SOLID and DRY principles, and ORM designs in general. You will also find code smells about global constants, but this would only be true if you are in a window context. I see you are already on the right path trying to avoid code duplication smell and the shotgun surgery smell. :-)
Good luck and don't hesitate to ask further questions and add additional details in comments!
If you'd provide more of your original version of code, that'd be more points to point to.
At the moment I just can say that the DRY principle is generally a good idea (not always but... well it's a complicated topic). There's a lot of articles about DRY. Google it.
You're afraid that your code became more complex. Don't be. In my experience, novice programmers fail miserably exactly because they discard the DRY principle. And only after a while, when they become stronger they start to fail at the KISS principle. And only one extra argument in addition to the 3 that already there doesn't add much to the complexity.
Am I bordering anti-pattern with the dynamic functions?
To be "dynamic" - that's the reason functions exist
What if I had 10+ modules using the same API?
Exactly! 10+ more chances to make a typo, to forget something, to misread, etc and 10+ more work to do if you'll need to change something. That is if you don't DRY your code.
PS. And the name getAllDocuments is actually better than getAllDocType1s if that's the real name from your original code.
While I share most of x00's answer, I would take into account how static your endpoints really.
Is there no chance module "X" can change any of its endpoints definitions? For instance, you need to pass one more query param. Are your modules all exactly the same with no room for change?
If the answer is no, there you go. To make that simple change you would have to refactor your whole code base (if you would implement it the way you propose, that is).
If the answer is yes, well, I see no reason for you to not implement your proposed dynamic functions. Personally, I would lean towards a service that my modules extend and use, just in case I do want to make minimal changes to them. For instance:
class MyGenericService {
constructor() {
this.url = window.config.baseUrl;
}
async getAllDocuments(config) {
return http.get(this.url, config)
.then(response => response.data);
.catch(...);
};
// ...and so on
}
This allows my code to scale and be modifiable, with just one takeaway which is you would need to maintain one file per module, that has something like this:
class XService extends MyGenericService {
constructor() {
this.url = window.config.baseUrl + '/v1/x';
}
}
If maintaining this extra files is too much overhead, you could receive the endpoint's URL in the constructor, on the MyGenericService, and you would just need to do stuff like this in your controllers:
const myXService = new MyGenericService('/v1/x');
const myYService = new MyGenericService('/v1/y');
// ...or it could use your endpoint url mapping
// I don't really know how is your code structured, just giving you ideas
There you have some options, hope it helps!
I have a service and I have 2 solutions from this kind of problem and I want to know what is best and when to use the Subject solution above the Service solution.
I have a UserModel that all my components see with my service, the approach that I want is when I change the UserModel from service, changes it for all my application
1 FIRST SERVICE
export class UserService {
private userModel: UserModel = new UserModel();
public userSubject$ = new Subject<any>();
private timeOut = 20000;
private mainConfig: MainConfig;
constructor(private http: HttpClient) {
this.mainConfig = new MainConfig();
}
getUserModel() {
return this.userModel;
}
setUserModel(user) {
this.userModel = user
}
}
And is just to make this call in my HTML from all my components and will work
this.userService.getUserModel().name
The second approach
2 SECOND SERVICE
#Injectable()
export class UserService {
private userModel: UserModel = new UserModel();
public userSubject$ = new Subject<any>();
private timeOut = 20000;
private mainConfig: MainConfig;
constructor(private http: HttpClient) {
this.mainConfig = new MainConfig();
}
getUserModel() {
return this.userModel;
}
setUserModel(user) {
this.userSubject$.next(this.userModel = user);
}
}
And in my HTML file, I just use
{{ userModel.name }}
And I must make this new line on my example-component.ts
ngOnInit
this.subTemp = this.userService.userSubject$.subscribe(
user => this.userModel = user
);
ngOnDestroy
this.subTemp.unsubscribed();
What is the advantage to make the Subject from direct from Service? Because I need to make much more work
If I could paraphrase your question(s), I'm guessing it'd go something like:
Why should I use Angular Services instead of just making async/http calls directly from the component?/Why should I write Service logic in a separate file as a dependency?
and
Why should I use lifecycle methods like ngOnInit and ngOnDestroy in conjunction with Services or async/http calls?
When it comes to questions like these, the Angular framework is more opinionated than other SPA technologies like React, Vue, etc. So while you're not technically forced to follow either of the approaches you listed, you should know of the downsides and problems that emerge if you follow the first approach rather than the traditional injectable Service approach(number 2).
Generally speaking, the Angular team recommends following a unidirectional data flow pattern in your app implemented with Services. This means that data flow should generally come from Services which distribute the data to components and then to view templates.
Within this pattern, there's also an implication of separation of concerns which is a good practice to follow within any app. Services should handle fetching and handling data, components should handle view logic, and templates should be as clean and declarative as possible. Components and their templates should consume data that's been processed already. Relatedly, you should try to keep your components as pure as possible - meaning they produce as few side effects as possible. This is because components are dynamically mounted and unmounted in the course of a user session. Have a look at this article for more information on pure components.
Aside from the above architectural discussion of Services there are some other, more concrete consequences to be aware of:
Failure to unsubscribe from observables can lead to memory leaks in your application. With the first scenario you've outlined above, a component may be loaded 10-20 times in a user session and each time you're setting up a new subscription without tearing it down again. This can have a very real performance impact on your app.
The Angular compiler is optimized to add and remove dependencies dynamically, resulting in better app performance. If you keep all your Service code right in your component, they'll be larger and slower. From a UX perspective, components should be as light and nimble as possible so they can load quickly for the user.
If you register a service as a provider, the Angular compiler will treat it as a singleton meaning there can be only one instance of it. This is as opposed to the many instances of a Service class generated with each component if you were to use the first approach you listed. This is another performance benefit of using injectable Services.
The Angular compiler is optimized to work with the DI framework so your next step may be to learn more about it and the implications of going with one approach or the other. There's a long talk about creating your own Angular Compiler that's a couple years old now that might be helpful.
What you wish to know is the difference between pull based method vs push based method of retrieving data.
Method 1: pull based
As the name suggests the pull based method is traditional method where you for eg. call a function and it returns the value once. If you need the value again, the function should be called again. And you exactly when the data will arrive.
export class UserService {
private userModel: UserModel = new UserModel();
getUserModel() {
return this.userModel;
}
setUserModel(user) {
this.userModel = user
}
}
some.component.ts
export class SomeComponent implements OnInit {
userModel: UserModel;
constructor(private _userService: UserService) { }
ngOnInit() {
// It's a one time call and you control when you get (or `pull`) the data
this.userModel = this._userService.getUserModel();
}
}
Method 2: push based
Here the observable decides when you receive the data. This is the basic of reactive/asynchronous data flow. You subscribe to the data source and wait till it pushes the data. You have no knowledge when the result might arrive.
#Injectable()
export class UserService {
public userSubject$ = new Subject<any>();
getUserModel() {
return this.userSubject$.asObservable();
}
setUserModel(user) {
this.userSubject$.next(this.userModel = user);
}
}
some.component.ts
export class SomeComponent implements OnInit, OnDestroy {
userModel: UserModel;
closed$ = new Subject<any>();
constructor(private _userService: UserService) { }
ngOnInit() {
// The stream is open until closed and the service/observable decide when it sends (or `pushes`) the data
this._userService.getUserModel().pipe(
takeUntil(this.closed$) // <-- close the `getUserModel()` subscription when `this.closed$` is complete
).subscribe(
userModel => { this.userModel = userModel }
);
}
ngOnDestroy() {
this.closed$.next();
this.closed$.complete();
}
}
Angular uses observables extensively due to the nature of data flow in a typical web-application and the flexibility it provides.
For eg. the HTTP client returns an observable that you can latch on to and wait till the server returns any information. And the RxJS provides numerous operators and functions to refine and adjust the data flow.
Let's say I have two classes, where you can observe over some observables.
First example, with public subject:
class EventsPub {
public readonly onEnd = new Subject<void>();
}
Second example, with private subject and registering method:
class EventsPriv {
private readonly endEvent = new Subject<void>();
public onEnd(cb: () => void): Subscription {
return this.endEvent.subscribe(cb);
}
}
The first example is somehow unsafe because anyone can call eventsPub.endEvent.next() from outside the class and introduce side effects, however, comparing to example 2 It allows for pipes, which is a big plus since developers can for ex. register only for the first event with eventsPub.onEnd.pipe(first()).subscribe(cb).
The second example also allows for one-time subscription but requires more code and ugly unsubscribing.
const subscription = eventsPriv.onEnd(() => {
// logic..
subscription.unsubscribe()
});
From your point of view, which is the best way to go? Or maybe there is a better solution?
This is based a lot on my personal preference but I'd do it like this:
class EventsPriv {
private readonly endEvent = new Subject<void>();
get endEvent$(): Observable<void> {
return this.endEvent;
}
}
So inside the class I'll use endEvent while I can still use it eg. in a template with obj.endEvent$ | async and from the outside it behaves like an Observable.
Note, that in fact I'm returning the same instance of Subject. The only thing that restricts the outside world from misusing it with obj.endEvent$.next() are Typescript's type guards. If I was using just JavaScript or if I typecasted it to any I could call next.
This is actually the recommended way of exposing Subjects instead of using the asObservable() operator. You can notice that this is used everywhere internally in RxJS 5. For example if you look at repeatWhen synopsys:
public repeatWhen(notifier: function(notifications: Observable): Observable): Observable
You can see that the notifier function receives an Observable as a parameter (you can see it in the code here as well https://github.com/ReactiveX/rxjs/blob/5.5.6/src/operators/repeatWhen.ts#L29).
But if you look into the code where the function is called you'll see they are in fact passing a Subject and not an Observable: https://github.com/ReactiveX/rxjs/blob/5.5.6/src/operators/repeatWhen.ts#L114-L115.
This has been discussed on RxJS GitHub page and reasons for this are performance and that the Typescript type guards are sufficient. You can read more in these discussions:
https://github.com/ReactiveX/rxjs/pull/2408
https://github.com/ReactiveX/rxjs/issues/2391
I have a small, home-brewed implementation of the Flux pattern that I'm working with, just to get a better understanding of the pattern. Its working quite well, and I'm learning a ton! But I've run into an issue I can't wrap my head around at the moment. Apologies in advance if this has an obvious solution.
Imagine my simple Store called ExampleStore, and my simple Component ExampleComponent. In it there are:
_exampleState
getExampleState()
setExampleState()
in ExampleComponent, to stay updated:
_onChange: function() {
setState({exampleState: ExampleStore.getExampleState()})
}
in ExampleStore, after a dispatched action I use the setter:
setExampleState(newStateFromDispatchedAction);
This works perfectly. The data is flowing as it should. But I have a question, because it seems easy to break the pattern because there is no privacy enforced on my _exampleState within my Store. Since I have a getter and private setter method for _exampleState, it seems natural to me that somehow I want to enforce more privacy on the variable. Is there a nifty way to do this, that I am overlooking?
As it is now, if, for example, inside ExampleComponent I do (which I wouldn't, but I could):
this.state.exampleState = {field:'example'}
//doing this
this.state.exampleState.field = 'example2';
//changes the store _exampleState to {field:'example2'}
I have changed the value of _exampleState within ExampleStore directly, without making use of the setter. This seems dangerous (and makes me question why I'd have a private setter/public getter to begin with). This question comes after dealing with a pesky bug where a library I was using modified the state variable directly, and thereby within the Store.
Is there some good way I'm overlooking to enforce privacy on the state variables in my Store, so that they may not be changed directly through their references in ExampleComponent? Sorry if this is a dumb question and I'm overlooking something simple, thanks for the help!
Be aware that one of the basic principles of the Flux philosophy is that stores should have no (public) setters. That means you should not be able to modify the store's state if not inside the store itself.
One way of enforcing the privacy could be by keeping state variables as private, only letting the store managing them.
EDIT: to "enforce" privacy, you could also return a deep copy of your state, as it is shown in the code.
The following code, based on the official flux GitHub repository's flux-todomvc example, highlights the idea:
var AppDispatcher = require('../dispatcher/AppDispatcher');
var AppConstants = require('../constants/AppConstants');
var EventEmitter = require('events').EventEmitter;
var assign = require('object-assign');
// This is a private state variable that can only be accessed in this file
var _exampleState = {/*...*/};
var ExampleStore = assign({}, EventEmitter.prototype, {
EXAMPLE_STATE_CHANGED: 'EXAMPLE_STATE_CHANGED',
// return a deep copy of your state so there is no way
// to modify the store's state by reference
getExampleState: function() {
return deepCopy(_exampleState);
}
/*...*/
};
// this is a private method (setter)
var _setExampleState = function(newExampleState) {
_exampleState = newExampleState;
};
ExampleStore.dispatchToken = AppDispatcher.register(function(action) {
switch(action.actionType) {
case AppConstants.CHANGE_EXAMPLE_STATE:
_setExampleState(action.newExampleState);
ExampleStore.emit(ExampleStore.EXAMPLE_STATE_CHANGED);
break;
}
});
// the implementation of deepCopy is a developer's choice
// this version of it is very inefficient
var deepCopy = function(obj) {
return JSON.parse(JSON.stringify(obj));
}
module.exports = ExampleStore;
Facebook official examples are a good way to understand how to implement the core Flux concepts.
EDIT: this is a way of "enforcing" privacy of a state variable, but it is discouraged due to the clear loss of efficiency. I guess that the main idea here is that, even though you are able to do so in some situations, changing the store's state through reference is just against Flux. It is important to notice that this enforcement is not a reality in many big libraries. In React, for instance, it is possible to modify the state of a component directly, even though that is completely not recommended.
you can wrap your store in a closure, and provide getters and setters, to prevent accidental modification of your state.
https://facebook.github.io/immutable-js (or Mori or seamless-immutable) provides the means to prevent modifications to nested data, while avoiding the need to make defensive deep clones in your getExampleState method. However, it has a huge impact on your coding style and code base. It possibly works best with a functional coding style, as is encouraged by some flux implementations, like https://github.com/rackt/redux.
Another option is to make it clearer that you don't want the state to be modified by ensuring that non-library code only sees the store state in React 'props', rather than React 'state' - not modifying props should be second nature to a React developer anyway (other bugs will occur if they modify it). This can be done using a generic flux wrapper component, such as Facebook's 'Container' - https://facebook.github.io/flux/docs/flux-utils.html