#16 Start Of Securities Domain Modeling With Markets And Segments
Saturday, September 21, 2019
In this article we start by eliminating the implicit understanding of the shape of our state by requiring the use of string indexers whenever we want to access a property on that state. Once that is done we will define two new classes that represent the beginning of modeling securities within our application.
Parts
- Part 45: Adjusting Shares
- Part 44: Plan Percentages
- Part 43: Home Securities
- Part 42: Updating Plans
- Part 41: Plan Details View
- Part 40: Portfolio Getters
- Part 39: Portfolio Plan
- Part 38: Portfolio Category
- Part 37: Account Securities
- Part 36: Account Transfer
- Part 35: View Account Security
- Part 34: Updating Deposit
- Part 33: View Account Deposit
- Part 32: Display Account Details
- Part 31: Account Getters
- Part 30: Deposits And Securities
- Part 29: Add Accounts Details
- Part 28: Refactoring Accounts
- Part 27: Add Security Models
- Part 26: Edit Security Details
- Part 25: View Security Details
- Part 24: Navigating To Details
- Part 23: Getters Validation
- Part 22: Query Parameters
- Part 21: Tab Entries
- Part 20: Tab Header
- Part 19: List View
- Part 18: Vuex Getters
- Part 17: End Domain Model
- Part 16: Start Domain Model
- Part 15: Pop Routes
- Part 14: Push Routes
- Part 13: Removing Accounts
- Part 12: Vuex (Decorators)
- Part 11: Vuex (Accounts)
- Part 10: The App Bar (Settings)
- Part 9: Remove Consumer Rxjs
- Part 8: The App Bar (Back)
- Part 7: Structuring Our App
- Part 6: Animation Between Views
- Part 5: Navigation Fade
- Part 4: Navigation Requests
- Part 3: Fade Animations (cont.)
- Part 2: Fade Animations
- Part 1: Splash Screen
Changing the state constants
- root
- src
- store
- store-constants.ts
- store
- src
First up is changing to values of our routes and accounts state constants (a). Right now they are 'routes' and 'accounts' respectively and we carry around in the back of our minds the knowledge that they are chosen to be those values because we have properties on our state with those exact names. Instead of tacitly knowing what those values are we are going to make it explicit by changing the way access them.
store-constants.ts
...
export const STATE_ACCOUNTS = "STATE_ACCOUNTS";
export const STATE_ROUTES = "STATE_ROUTES";
Using the state constants themselves to define the state interface
- root
- src
- store
- store-types.ts
- store
- src
Now instead of writing the property names themselves in our state interface we are changing things so that in order to access them we need to use the appropriate string indexer (b).
store-types.ts
...
import {
STATE_ACCOUNTS,
STATE_ROUTES,
} from "@/store/store-constants";
export interface IStoreState {
[STATE_ACCOUNTS]: IAccountState;
[STATE_ROUTES]: IRouteState;
}
...
Fixing and modifying the account module
- root
- src
- store
- account-module.ts
- store
- src
With the changes that we have made to the state interface we now have to update our account module (c). In addition to fixing the way we access the properties we are also updating the names of the things that we are exporting and exporting the initial state itself. The point of changing the names and exporting the state is to make it both easier and more uniform to import into the store.
account-module.ts
...
import {
...
STATE_ACCOUNTS,
} from "@/store/store-constants";
...
import { initialState } from "@/store/account-initial-state";
...
export const accountActions: StoreActionTree = {
...
};
export const accountMutations: StoreMutationTree = {
[MUTATION_ADD_ACCOUNT](...) {
const account = new AccountModel({ id: state[STATE_ACCOUNTS].index, name: payload });
state[STATE_ACCOUNTS].items = [...state[STATE_ACCOUNTS].items, account].sort((a, b) => {
...
});
state[STATE_ACCOUNTS].index += 1;
},
[MUTATION_REMOVE_ACCOUNT](...) {
state[STATE_ACCOUNTS].items = state[STATE_ACCOUNTS].items.filter((x) => x.id !== payload);
},
};
export const accountState = {
[STATE_ACCOUNTS]: initialState,
};
Fixing and modifying the route module
- root
- src
- store
- route-module.ts
- store
- src
Next up we are going to mirror the changes that we made in the account module in the route module as well (d).
route-module.ts
...
import {
...
STATE_ROUTES,
} from "@/store/store-constants";
...
import { initialState } from "@/store/route-initial-state";
...
export const routeActions: StoreActionTree = {
...
};
export const mutations: StoreMutationTree = {
[MUTATION_POP_ROUTE](...) {
switch (state[STATE_ROUTES].history.length) {
...
case 1:
state[STATE_ROUTES].history = [];
return;
default:
state[STATE_ROUTES].history = state[STATE_ROUTES].history.slice(1);
return;
}
},
[MUTATION_PUSH_ROUTE](...) {
state[STATE_ROUTES].history = [payload, ...state[STATE_ROUTES].history];
},
};
export const routeState = {
[STATE_ROUTES]: initialState,
};
The store looks a lot cleaner and more organized now
- root
- src
- store
- store.ts
- store
- src
With the updates that we can now make to the store we can see the purpose of the changes and updates that we just made (e). To me things are now a lot cleaner and more organized.
store.ts
...
import {
accountActions,
accountState,
accountMutations,
} from "@/store/account-module";
import {
routeActions,
routeState,
routeMutations,
} from "@/store/route-module";
...
const actions = {
...accountActions,
...routeActions,
};
const mutations = {
...accountMutations,
...routeMutations,
};
const state: IStoreState = {
...accountState,
...routeState,
};
...
The last string indexer for now
- root
- src
- routing
- routing-service.ts
- routing
- src
The last thing we have to do before we can move on to something new is to update our route service to use the indexer that we specified when interacting with the route state (f).
routing-service.ts
...
import {
IStoreState,
STATE_ROUTES,
} from "@/store";
...
export class RoutingService {
...
public back = () => {
if (this._store.state[STATE_ROUTES].history.length === 0) {
return;
}
const to = this._store.state[STATE_ROUTES].history[0];
...
}
}
New state constants
- root
- src
- store
- store-constants.ts
- store
- src
Instead of jumping back and forth we are just going to take the time to specify a few new state constants that we are going to be needing shortly (g). These constants represent four new objects that we will be introducing to our state.
store-constants.ts
...
export const STATE_SECURITY_MARKETS = "STATE_SECURITY_MARKETS";
export const STATE_SECURITY_SEGMENTS = "STATE_SECURITY_SEGMENTS";
export const STATE_SECURITY_TERRITORIES = "STATE_SECURITY_TERRITORIES";
export const STATE_SECURITY_TYPES = "STATE_SECURITY_TYPES";
Enter the descriptor
- root
- src
- store
- security-descriptor.ts
- store
- src
For all of the objects that will be used in the states that are represented by the constants that we just added they will all, at least to start, consist of just an id and text property. Since this is the case, and to make life easier, we will start by defining an abstract class that they will all be able to inherit from (h). While we are here, in an effort to do a little future proofing, we will also define a method that we can call when we wish to persist our store to some long term storage such as a file on disk.
security-descriptor.ts
export abstract class SecurityDescriptor {
public get id() { return this._id; }
public get text() { return this._text; }
constructor(private readonly _id: number, private readonly _text: string) { }
public toPersistObj = () => {
return {
id: this._id,
text: this._text,
};
}
}
The market
- root
- src
- store
- security-market-model.ts
- store
- src
The first descriptor that we will define is the security market (i). As I mentioned previously everything we currently need from the market is provide by the base class.
security-market-model.ts
import { SecurityDescriptor } from "@/store/security-descriptor";
export class SecurityMarketModel extends SecurityDescriptor { }
Time for more constants
- root
- src
- store
- security-constants.ts
- store
- src
The main purpose for the constants that we will be defining in the security constants file is to allow more consistent creation of our initial state (j).
security-constants.ts
export const SECURITY_MARKET_CBOE = "CBOE CONSOLIDATED";
export const SECURITY_MARKET_NASDAQ = "NASDAQ";
export const SECURITY_MARKET_NYSE = "NYSE";
The market types
- root
- src
- store
- security-types.ts
- store
- src
The last thing that we have to do before creating the initial state is create the interface that will determine the shap of our market state within the overall store state (k).
security-types.ts
import {
STATE_SECURITY_MARKETS,
STATE_SECURITY_SEGMENTS,
STATE_SECURITY_TERRITORIES,
STATE_SECURITY_TYPES,
} from "@/store/store-constants";
import { SecurityMarketModel } from "@/store/security-market-model";
export interface ISecurityMarketsModelState {
index: number;
items: SecurityMarketModel[];
}
export interface ISecurityState {
[STATE_SECURITY_MARKETS]: ISecurityMarketsModelState;
}
The market initial state
- root
- src
- store
- security-market-initial-state.ts
- store
- src
Not much to say here we just need to populate our market state with some initial data (l).
security-market-initial-state.ts
import { SecurityMarketModel } from "@/store/security-market-model";
import { ISecurityMarketsModelState } from "@/store/security-types";
import {
SECURITY_MARKET_CBOE,
SECURITY_MARKET_NASDAQ,
SECURITY_MARKET_NYSE,
} from "@/store/security-constants";
const markets: SecurityMarketModel[] = [];
function createMarket(id: number, text: string) {
markets.push(new SecurityMarketModel(id, text));
return id += 1;
}
let index = 1;
index = createMarket(index, SECURITY_MARKET_NYSE);
index = createMarket(index, SECURITY_MARKET_NASDAQ);
index = createMarket(index, SECURITY_MARKET_CBOE);
export const initialState: ISecurityMarketsModelState = {
index,
items: markets,
};
The security segment
- root
- src
- store
- security-segment-model.ts
- store
- src
As expecting we just need to jump back in and follow the patter all over again. First up is creating the security segment model (m).
security-segment-model.ts
import { SecurityDescriptor } from "@/store/security-descriptor";
export class SecuritySegmentModel extends SecurityDescriptor { }
Adding the segment constants
- root
- src
- store
- security-constants.ts
- store
- src
Just like with the markets we are adding some constants that we can use when creating the initial state (n).
security-constants.ts
export const SECURITY_SEGMENT_DEVELOPED_TEXT = "Developed Markets";
export const SECURITY_SEGMENT_EMERGING_TEXT = "Emerging Markets";
export const SECURITY_SEGMENT_LARGE_CAP_TEXT = "Large Cap";
export const SECURITY_SEGMENT_MID_CAP_TEXT = "Mid Cap";
export const SECURITY_SEGMENT_SMALL_CAP_TEXT = "Small Cap";
Specifying the interfaces for the segment
- root
- src
- store
- security-types.ts
- store
- src
In our penultimate step for this article we need to define the interface for our segment state and add it to the security state (o).
security-types.ts
...
import { SecuritySegmentModel } from "@/store/security-segment-model";
...
export interface ISecuritySegmentModelState {
index: number;
items: SecuritySegmentModel[];
}
export interface ISecurityState {
...
[STATE_SECURITY_SEGMENTS]: ISecuritySegmentModelState;
}
The segment initial state
- root
- src
- store
- security-segment-initial-state.ts
- store
- src
Once again time to define our an initial state (p).
security-segment-initial-state.ts
import {
SECURITY_SEGMENT_DEVELOPED_TEXT,
SECURITY_SEGMENT_EMERGING_TEXT,
SECURITY_SEGMENT_LARGE_CAP_TEXT,
SECURITY_SEGMENT_MID_CAP_TEXT,
SECURITY_SEGMENT_SMALL_CAP_TEXT,
} from "@/store/security-constants";
import { SecuritySegmentModel } from "@/store/security-segment-model";
import { ISecuritySegmentModelState } from "@/store/security-types";
const segments: SecuritySegmentModel[] = [];
function createSegment(id: number, text: string) {
segments.push(new SecuritySegmentModel(id, text));
return id += 1;
}
let index = 1;
index = createSegment(index, SECURITY_SEGMENT_LARGE_CAP_TEXT);
index = createSegment(index, SECURITY_SEGMENT_MID_CAP_TEXT);
index = createSegment(index, SECURITY_SEGMENT_SMALL_CAP_TEXT);
index = createSegment(index, SECURITY_SEGMENT_DEVELOPED_TEXT);
index = createSegment(index, SECURITY_SEGMENT_EMERGING_TEXT);
export const initialState: ISecuritySegmentModelState = {
index,
items: segments,
};