I've been looking at the ngrx and redux pattern lately and am thinking how would I rewrite my existing Angular2 app into using ngrx/store.
What I have is an app where users can view and (if signed in) can like and publish citations.
A typical citation object looks like this:
{
text: "Success is the ability to go from one failure to another with no loss of enthusiasm.",
publisher: user12345,
rank: 14,
//some more data
}
Application strucure looks like the following:
Home page - either with registration/login form or with random citation (if signed in).
Profile page with tabs
Tab with all citations published by the user and a form to publish a new one.
Profile info
Citations feed page
Page to view other user's profile with similar structure as above. (when user clicks on the citation's publisher).
So, I'm quite frustrated of how would the AppState tree look like.
AppState {
UserState {
UserCitationsState,
UserInfoState,
AuthState
},
RouterState,
UserPageState //State representing the other user's page viewed
//etc
}
The main question is - what should I store in each state since all the data is fetched per-request from the backend REST api. Would it be just boolean values like e.g.:
UserPageState {
loading: false,
loaded: true
}
or should it also store all the information and just replace it every time a new user page is requested? As every time user navigates to some other's user page all the data is fetched from the backend.
That's the point of fundamental confusion for me - how to handle these kind of apps with redux.
EDIT
At the moment I limited myself with 5 states (5 reducers) to represent the overall app:
AuthState
UserState
UserListState
CitationState
CitationListState
However, in the overall app state I'm duplicating many of them. I guess it's fine. Or would there be an even better way?
export interface AppState
{
localUser: AuthState
//homePage
homeCitation: CitationState
//profilePage
profileInfo: UserState
profileCitations: CitationListState
favouriteCitations: CitationListState
subscribers: UserListState
//userPage (when local user navigates to citation publisher's profile)
userInfo: UserState
userCitations: CitationListState
userSubscribers: UserListState
//feedPage
feed: CitationListState
//.....
}
My initial thoughts on this to think of the application state much like you would a database.
I would structure using the following reducers:
AppState: {
CitationState
UserProfileState,
UserInfo,
RouterState
}
interface CitationState {
citations: Citation[]
}
interface UserProfileState {
userProfiles: UserProfile[]
}
interface UserInfo {
userInfo: UserInfo
}
interface Citation {
id: number;
publisherId (userId): number;
rank: number;
text: string;
}
interface UserProfile {
userId: number;
citationIds: number[]
}
interface UserInfo {
userId: number;
authToken: string;
}
Each smart component would then compose the data as necessary to render the view. For example, you can determine if the user profile is your own, by checking to see if the routed user profile matches the one in the UserInfo reducer.
Don't be concerned about creating loading/loading in state, this is something you could derive from the state of your store. Since all the data is observable, when you query from it, you are given the latest snapshot of available data.
Instead of binding to a loading property of the store when loading a user's citations, instead build a query for that data.
For example:
let userCitation$ = state.select(appState => appState.citations)
.map(citations => {
let userCitation = citations.find(c => c.id === userId);
return {
loaded: !!userCitation,
userCitation: userCitation
};
});
Related
I'm new to flutter and I have experience in web application using state managements like Redux or Vuex where the initial state of a module might be something like:
{
value1: 0,
value2: 10,
aBool: false,
aString: 'Hello'
}
Then based on Reducers or Mutations we can update a single or multiple properties of the state.
Now, learning Flutter I decided to use Bloc/Cubit and online I cannot find the right answer to my problem, even because the majority of the example are always based on the crappy counter app and never on a more realistic scenario.
All I can see is something based on 4 states in Bloc: initial, loading, success and error.
This is fine when fetching data from an API, but what if my state has also more properties?
how to update those properties?
Actually I created my test Cubit to fetch something from my API, it works. Now I wish to add more properties on the state and update it based on actions, how can I do that?
Example state:
#freezed
abstract class TestState with _$TestState {
const factory TestState.initial() = _Initial;
const factory TestState.loading() = _Loading;
const factory TestState.success(UserData user) = _Success;
const factory TestState.error(String message) = _Error;
}
Example Cubit:
class TestCubit extends Cubit<TestCubit> {
TestCubit(this._testClient)
: super(TestState.initial());
final TestClient _testClient;
String greet = 'Hi';
Future<void> testFetchData() async {
...
emit(TestState.success(testData));
...
}
}
I can successfully handle the varioud initial, loading, etc... states.
I can correctly watch at the greet property: context.read<TestCubit>().greet
How should I now update that value with 'hello!'?
// TestCubit
updateGreet(String text) {
emit(I don't know);
}
I omitted all my various tries to update that value.
Thanks
I am learning CQRS and event sourcing architecture (with nestjs), and I am a bit confused by aggregates.
I am following the kind of architecture explained here : https://danielwhittaker.me/2020/02/20/cqrs-step-step-guide-flow-typical-application/
If I understand well, the aggregate root is my "Write Model". It update itself using events which will be committed to the event bus, and I get its current state using event history (or cache).
As I never really read the data in the aggregate root (only the data needed to accept or not the next commands), and I don't persist it (the events are), should I really need to keep all my data in aggregates?
I am not sure if I am clear, so let's see a simplified example :
I've got a CreateProduct command for my shopping website, and a ProductCreated event. I use the content of the event to create views for some query like GetProductByCategory, SearchProduct, ...
Here the command :
class CreateProduct {
public name: string;
public description?: string;
// ...
}
I skip the commandHandler. If I understand well, my aggregate root should be like :
class ProductAggregateRoot extends AggregateRoot {
public id: string;
private name: string;
private description?: string;
create(data: { name: string, description?: string }) {
if (! data.name) {
throw Error('Name is required');
}
this.apply(new ProductCreated(uuid(), data));
}
onProductCreated(event: ProductCreated) {
this.id = event.id;
this.name = event.name;
this.description = event.description;
}
}
Can I just do :
class ProductAggregateRoot extends AggregateRoot {
public id: string
create(data: { name: string, description?: string }) {
if (! data.name) {
throw Error('Name is required');
}
this.apply(new ProductCreated(uuid(), data));
}
onProductCreated(event: ProductCreated) {
this.id = event.id;
}
}
as I never use name and description on the command side? It is just usefull for me to create the views on the query side.
It confuses me because it seems to be far from the domain (a Product is more than just an id). But I don't get the point to keep these data here. If I change my mind, I can add it later and rebuild my aggregate roots from the history.
I do not know about nestjs in particular, but in a general implementation of an Event Sourcing application is absolutely OK to only us the fields you need in order to satisfy your business rules. So in this case, since there are no rules involving name or description they don't need to be materialized into the aggregate root class when you handle additional commands (maybe DeleteProduct or similar).
When you apply your next command your application should materialize the aggregate root again from the history of events, so yes you can add fields later if needed.
You can see an example from the Serialized Java client here (https://serialized.io/java/working-with-aggregates/) where OrderPlaced event contains an amount that is not read into the transient state of the Order aggregate when handling commands.
We have a basic app state that needs to be persisted upon the browser refresh. similar to vuex-state-persistance plugin. Here is the basic state code that needs to be persisted.
export const initialState = {
user: {
uuid: 'wedRfertYjsnjnakUiisdj878HBhsns',
name: 'Kiran Maniya',
scope: 'user'
}
};
Is there anything that can be used directly as a plugin or I need to write a custom plugin that persists the state in localStorage asynchronously? Also, how do we modularise the state when we have a complex and large state to manage?
Aurelia Store provides a built-in mechanism to persist state in localStorage.
So, if your initialState goes initialized in main.ts or main.js something like this:
aurelia.use.plugin('aurelia-store', { initialState: initialState });
Then in app.ts or .js you should register the localstorage middleware and perform a rehydration.
So, in app constructor, you could write:
import { localStorageMiddleware, rehydrateFromLocalStorage, Store } from 'aurelia-store';
import { initialState, State } from 'state';
...
constructor(
private store: Store<State>,
) {
store.registerMiddleware(localStorageMiddleware, MiddlewarePlacement.After, { key: ¨someKey¨ });
store.registerAction('Rehydrate', rehydrateFromLocalStorage);
}
Regarding modularization, maybe you should combine store and the EventAggregator to implement more complex scenarios.
Good luck
I was watching a talk about Reactjs/Flux during which they showed a code inside Facebook app that is used to preview user profile picture.
Link to the Youtube video.
Snippet:
class ProfilePicture extends React.component{
render(){
let info = usersStore.getUser(this.props.id);
return (<img src={info.uri} />);
}
}
This little snippet had me thinking about how do they implement getUser inside the user store?
I understand that Flux has multiple stores while Flux keeps a single source. Yet such snippet had me thinking that may be.. if I'm fetching a comment for the server the returned value is something like:
/posts
[
{
id:1,
title: 'This is post title',
userId:1,
userName: 'User name',
picture: 'http://user.picture',
}
]
Tet the above ProfilePicture component snippet doesn't read user info from the Post object, it reads it from users Store object, so that made lot sense to me since in my app a user typically have access to <200 other users. So maybe I should stop returning user info with every post. And instead I'd use a user store that store all users info, cache them inlocal storage, retrieve them from server, if needed.
So I'm considering changing my API to respond for posts to:
/post : select id,title from posts;
[
{id:1,title:'Post title'}
]
And have another endpoint that return users info to be loaded lazily by Redux.
So in my Redux I need to change initial state from:
InitialState = {
posts:[] //where all posts and the user who posted this post is stored
}
Into:
InitialState = {
posts:[] //posts and userId only
users:[],//users data <---- here i need a getter function
}
Now one way I can populate the users store is during app initialization. But is this the right timing to do it while app starts up or should I wait until a post component is loaded first, and needs to access data of specific user/s then I'd load the users?
Is it possible for Redux to have getters setters?
class ProfilePicture extends React.component{
render(){
let info = usersStore.getUser(this.props.id);
return (<img src={info.uri} />);
}
}
** Why are they using usersStore.getUser instead of just passing the user by id to the mapStateToProps in the connect function of Redux ?
I am working on a server component which is responsible for caching models in memory and then stream any changes to interested clients.
When the first client requests a model (well model key, each model has a key to identify it) the model will be created (along with any subscriptions to downstream systems) and then sent to the client, followed by a stream of updates (generated by downstream systems). Any subsequent client's should get this cached (updated) model, again with the stream of updates. When the last client unsubscribes to the model the downstream subscriptions should be destroyed and the cached model destroyed.
Could anyone point me in the right direction as regards to how Rx could help here. I guess what isn't clear to me at the moment is how I synchronize state (of the object) and the stream of changes? Would I have two separate IObservables for the model and updates?
Update: here's what I have so far:
Model model = null;
return Observable.Create((IObserver<ModelUpdate> observer) =>
{
model = _modelFactory.GetModel(key);
_backendThing.Subscribe(model, observer.OnNext);
return Disposable.Create(() =>
{
_backendThing.Unsubscribe(model);
});
})
.Do((u) => model.MergeUpdate(u))
.Buffer(_bufferLength)
.Select(inp => new ModelEvent(inp))
.Publish()
.RefCount()
.StartWith(new ModelEvent(model)
If I understood the problem correctly, there are Models coming in dynamically. At any point in time in your Application's lifetime, the number of Models are unknown.
For that purpose an IObservable<IEnumerable<Model>> looks like a way to go. Each time there is a new Model added or an existing one removed, the updated IEnumerable<Model> would be streamed. Now it would essentially preserve the older objects as opposed to creating all Models each time there is an update unless there is a good reason to do so.
As for the update on each Model object's state such as any field value or property value changed, I would look into Paul Betts' ReactiveUI project, it has something called ReactiveObject. Reactive object helps you get change notifications easily, but that library is mainly designed for WPF MVVM applications.
Here is how a Model's state update would go with ReactiveObject
public class Model : ReactiveObject
{
int _currentPressure;
public int CurrentPressure
{
get { return _currentPressure; }
set { this.RaiseAndSetIfChagned(ref _currentPressure, value); }
}
}
now anywhere you have Model object in your application you could easily get an Observable that will give you updates about the object's pressure component. I can use When or WhenAny extension methods.
You could however not use ReactiveUI and have a simple IObservable whenever a state change occurs.
Something like this may work, though your requirements aren't exactly clear to me.
private static readonly ConcurrentDictionary<Key, IObservable<Model>> cache = new...
...
public IObservable<Model> GetModel(Key key)
{
return cache.GetOrAdd(key, CreateModelWithUpdates);
}
private IObservable<Model> CreateModelWithUpdates(Key key)
{
return Observable.Using(() => new Model(key), model => GetUpdates(model).StartWith(model))
.Publish((Model)null)
.RefCount()
.Where(model => model != null);
}
private IObservable<Model> GetUpdates(Model model) { ... }
...
public class Model : IDisposable
{
...
}