cleanup, patterns
This commit is contained in:
@@ -1,14 +1,8 @@
|
||||
import { bind, ok, tap, tapError, pipe } from '../library/result.js';
|
||||
import { output, outputError } from '../library/logging.js';
|
||||
import { bind, ok, pipe } from '../library/result.js';
|
||||
import { log, logWith, formatMoney } from '../library/logging.js';
|
||||
import { withdraw } from '../domain/account.js';
|
||||
import { createMoney } from '../domain/money.js';
|
||||
|
||||
const log = (src, msg) => (result) =>
|
||||
tap((x) => output(src, msg)(x))(tapError((e) => outputError(src, e))(result));
|
||||
|
||||
const logWith = (src, renderText) => (result) =>
|
||||
tap((x) => output(src, renderText(x))(x))(tapError((e) => outputError(src, e))(result));
|
||||
|
||||
export const createWithdrawMoney = (repo) =>
|
||||
ok((accountId, amount) =>
|
||||
pipe(
|
||||
@@ -16,7 +10,7 @@ export const createWithdrawMoney = (repo) =>
|
||||
log('App load', 'Account loaded'),
|
||||
log('App exec wdrwl', `[App] Executing withdrawal of ${amount.toFixed(2)}...`),
|
||||
bind((account) => withdraw(account, createMoney(amount))),
|
||||
logWith('App wdrwl done', (a) => `Withdrawal applied. New balance: ${a.balance.amount.toFixed(2)}`),
|
||||
logWith('App wdrwl done', (a) => `Withdrawal applied. New balance: ${formatMoney(a.balance)}`),
|
||||
bind(repo.saveAccount),
|
||||
log('App acc svd', '[App] Account persisted.')
|
||||
)
|
||||
|
||||
+15
-12
@@ -1,4 +1,5 @@
|
||||
import { ok, fail } from '../library/result.js';
|
||||
import { match, when, any } from '../library/patterns.js';
|
||||
|
||||
export const openingBalanceMustBeNonNegative = { type: 'OpeningBalanceMustBeNonNegative' };
|
||||
|
||||
@@ -20,15 +21,17 @@ export const openAccount = (id, openingBalance) =>
|
||||
? fail(openingBalanceMustBeNonNegative)
|
||||
: ok({ id, balance: openingBalance });
|
||||
|
||||
export const withdraw = (account, amount) => {
|
||||
if (amount.amount <= 0) {
|
||||
return fail(amountMustBePositive);
|
||||
}
|
||||
if (account.balance.amount < amount.amount) {
|
||||
return fail(insufficientBalance(account.balance, amount));
|
||||
}
|
||||
return ok({
|
||||
...account,
|
||||
balance: { amount: account.balance.amount - amount.amount },
|
||||
});
|
||||
};
|
||||
export const withdraw = (account, amount) =>
|
||||
match(
|
||||
when(({ amount: a }) => a.amount <= 0, () => fail(amountMustBePositive)),
|
||||
when(
|
||||
({ account: acc, amount: amt }) => acc.balance.amount < amt.amount,
|
||||
({ account: acc, amount: amt }) => fail(insufficientBalance(acc.balance, amt))
|
||||
),
|
||||
when(any, ({ account: acc, amount: amt }) =>
|
||||
ok({
|
||||
...acc,
|
||||
balance: { amount: acc.balance.amount - amt.amount },
|
||||
})
|
||||
)
|
||||
)({ account, amount });
|
||||
|
||||
+4
-10
@@ -1,17 +1,11 @@
|
||||
import { randomUUID } from 'crypto';
|
||||
import { catchResult, bind, tap, tapError, mapError, pipe } from './library/result.js';
|
||||
import { output, outputError } from './library/logging.js';
|
||||
import { catchResult, bind, mapError, pipe } from './library/result.js';
|
||||
import { log, logWith, formatMoney } from './library/logging.js';
|
||||
import { createInMemoryRepository, innerAccountError } from './infrastructure/repository.js';
|
||||
import { openAccount } from './domain/account.js';
|
||||
import { createMoney } from './domain/money.js';
|
||||
import { createWithdrawMoney } from './application/accountApp.js';
|
||||
|
||||
const log = (src, msg) => (result) =>
|
||||
tap((x) => output(src, msg)(x))(tapError((e) => outputError(src, e))(result));
|
||||
|
||||
const logWith = (src, renderText) => (result) =>
|
||||
tap((x) => output(src, renderText(x))(x))(tapError((e) => outputError(src, e))(result));
|
||||
|
||||
console.log('[js-fp3] Starting withdraw money demo...');
|
||||
|
||||
const accountId = randomUUID();
|
||||
@@ -30,11 +24,11 @@ pipe(
|
||||
bind((withdrawMoney) =>
|
||||
pipe(
|
||||
openAccount(accountId, createMoney(200)),
|
||||
logWith('js-fp3 seed', (account) => `Seeding account ${account.id} with opening balance ${account.balance.amount.toFixed(2)}`),
|
||||
logWith('js-fp3 seed', (account) => `Seeding account ${account.id} with opening balance ${formatMoney(account.balance)}`),
|
||||
bind(repo.saveAccount),
|
||||
log('js-fp3 exec', `Executing withdrawal ${withdrawalAmount.toFixed(2)} from account ${accountId}`),
|
||||
bind(() => withdrawMoney(accountId, withdrawalAmount)),
|
||||
logWith('js-fp3 new balance', (account) => `New balance is ${account.balance.amount.toFixed(2)}`),
|
||||
logWith('js-fp3 new balance', (account) => `New balance is ${formatMoney(account.balance)}`),
|
||||
mapError((ae) => innerAccountError(ae))
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ok, fail } from '../library/result.js';
|
||||
import { logReturn } from '../library/logging.js';
|
||||
import { logReturn, formatMoney } from '../library/logging.js';
|
||||
import { accountNotFound } from '../domain/account.js';
|
||||
|
||||
export const repositoryCreationFailed = (message) => ({
|
||||
@@ -29,7 +29,7 @@ export const createInMemoryRepository = () => {
|
||||
const save = (account) => {
|
||||
store.set(account.id, account);
|
||||
return logReturn(
|
||||
`[Repo] Saved account ${account.id} with balance ${account.balance.amount.toFixed(2)}`,
|
||||
`[Repo] Saved account ${account.id} with balance ${formatMoney(account.balance)}`,
|
||||
ok(account)
|
||||
);
|
||||
};
|
||||
|
||||
Vendored
+11
@@ -9,6 +9,17 @@ export declare const outputWith: <T>(
|
||||
|
||||
export declare const outputError: <E>(src: string, error: E) => void;
|
||||
|
||||
export declare const logSuccess: <T, E>(src: string, message: string) => (
|
||||
result: import('./result.js').Result<T, E>
|
||||
) => import('./result.js').Result<T, E>;
|
||||
|
||||
export declare const logSuccessWith: <T, E>(
|
||||
src: string,
|
||||
renderText: (x: T) => string
|
||||
) => (result: import('./result.js').Result<T, E>) => import('./result.js').Result<T, E>;
|
||||
|
||||
export declare const formatMoney: (money: { amount: number }) => string;
|
||||
|
||||
export declare const log: <T, E>(src: string, message: string) => (
|
||||
result: import('./result.js').Result<T, E>
|
||||
) => import('./result.js').Result<T, E>;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { tap, tapError } from './result.js';
|
||||
import { tap, tapError, pipe } from './result.js';
|
||||
import { formatMoney as formatMoneyFromDomain } from '../domain/money.js';
|
||||
|
||||
export { formatMoneyFromDomain as formatMoney };
|
||||
|
||||
export const logReturn = (message, r) => {
|
||||
console.log(message);
|
||||
@@ -19,6 +22,12 @@ export const outputError = (src, error) => {
|
||||
console.error(`\x1b[1;31m[${src} ERROR]\x1b[0m ${value}`);
|
||||
};
|
||||
|
||||
export const log = (src, message) => tap(output(src, message));
|
||||
export const logSuccess = (src, message) => tap(output(src, message));
|
||||
|
||||
export const logWith = (src, renderText) => tap(outputWith(src, renderText));
|
||||
export const logSuccessWith = (src, renderText) => tap(outputWith(src, renderText));
|
||||
|
||||
export const log = (src, message) => (result) =>
|
||||
pipe(result, tap(output(src, message)), tapError((e) => outputError(src, e)));
|
||||
|
||||
export const logWith = (src, renderText) => (result) =>
|
||||
pipe(result, tap((x) => output(src, renderText(x))(x)), tapError((e) => outputError(src, e)));
|
||||
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
export type Predicate<T> = (value: T) => boolean;
|
||||
export type Handler<T, R> = (value: T) => R;
|
||||
export type Branch<T, R> = [Predicate<T>, Handler<T, R>];
|
||||
|
||||
export declare const match: <T, R>(...branches: Branch<T, R>[]) => (value: T) => R;
|
||||
|
||||
export declare const when: <T, R>(predicate: Predicate<T>, handler: Handler<T, R>) => Branch<T, R>;
|
||||
|
||||
export declare const any: Predicate<any>;
|
||||
@@ -0,0 +1,10 @@
|
||||
export const match = (...branches) => (value) => {
|
||||
for (const [predicate, handler] of branches) {
|
||||
if (predicate(value)) return handler(value);
|
||||
}
|
||||
throw new Error('No matching pattern');
|
||||
};
|
||||
|
||||
export const when = (predicate, handler) => [predicate, handler];
|
||||
|
||||
export const any = () => true;
|
||||
Reference in New Issue
Block a user