3.7.3 • Published 1 year ago

@baloise/web-app-clean-architecture v3.7.3

Weekly downloads
-
License
Apache-2.0
Repository
github
Last release
1 year ago

@baloise/web-app-clean-architecture

Continuous Release npm npm bundle size npm GitHub GitHub issues

Installation

A small TypeScript library to follow the clean architecture pattern.

npm install @baloise/web-app-clean-architecture

Domain

In the domain layer a DomainError can be thrown. Use the DomainError to improve debugging and the stack trace.

Define a custom error with extends the imported DomainError.

import { DomainError } from '@baloise/web-app-clean-architecture'

export class MaxShoppingCartItemAmountError extends DomainError {
  constructor(...params: any[]) {
    super('MaxShoppingCartItemAmountError', ...params)
  }
}

In the domain file import the custom domain error and throw it like a normal error.

import { CartItem, createCartItem } from './CartItem'
import { MaxShoppingCartItemAmountError } from './error/MaxShoppingCartItemAmountError'
import { Pizza } from './Pizza'

export class Cart {
  constructor(public readonly items: CartItem[] = []) {}
}

export function createCart(cart?: Partial<Cart>): Cart {
  const { items } = { ...new Cart(), ...cart }
  return new Cart(items)
}

export function addPizza(cart: Cart, pizza: Pizza): Cart {
  const newCart = createCart(cart)
  const index = findIndex(newCart, pizza)

  if (index >= 0) {
    const { amount } = newCart.items[index]
    const newAmount = amount + 1

    if (newAmount > 10) {
      throw new MaxShoppingCartItemAmountError()
    }

    newCart.items[index] = createCartItem({ pizza, amount: newAmount })
  } else {
    newCart.items.push(createCartItem({ pizza, amount: 1 }))
  }

  return newCart
}

Service

UseCase

The UseCase Context is what is passed in the execute method. The second generic type Pizza[] defines the value of the returned Result object.

interface Context {
  pizza: Pizza
}

export class AddPizzaUseCase implements UseCase<Context, Pizza[]> {
  async execute(context): Promise<Result<Pizza[], string>> {
    console.log(context.pizza)
    ...
    return Result.ok([])
  }
}

To inject adapters or ports use the constructor of the UseCase.

interface Context {}

export class FetchAllPizzasUseCase implements UseCase<Context, Pizza[]> {
  constructor(private readonly api: PizzaApi) {}

  async execute(): Promise<Result<Pizza[], string>> {
    const result = await this.api.getAll()

    if (result.isSuccess) {
      const json = await result.value()
      const pizzas = json.map((item: Pizza) => createPizza(item))
      return Result.ok(pizzas)
    } else {
      return Result.fail('Could not load pizzas form server')
    }
  }
}

Result

Result is used to identify if a function was successful or failed. And most importantly, do not throw errors.

First we have a function to load some pizza objects form the server. In the function we return a Result instance.

export async function fetchAllPizzas() {
  const response = await fetch('/api/pizzas')

  if (response.status === 200) {
    const data = await response.json()
    return Result.ok(data)
  }

  return Result.fail('Could not load pizzas form server')
}

Now we call the defined fetchAllPizzas function, which return a instance of Result.

With result.isSuccess we can easily check if the process was successful.

const result = await fetchAllPizzas()

if (result.isSuccess) {
  // continue with the happy flow
} else {
  // do some error handling
}
3.7.1-beta.302

1 year ago

3.7.1-beta.304

1 year ago

3.7.1-beta.303

1 year ago

3.7.1-beta.305

1 year ago

3.7.3

1 year ago

3.6.4

2 years ago

3.6.3

2 years ago

3.7.2-beta.1

1 year ago

3.7.0

2 years ago

3.6.2

2 years ago

3.6.1

2 years ago

3.6.0

2 years ago

3.5.0

2 years ago

3.3.2-beta.24

2 years ago

3.3.2-beta.23

2 years ago

3.3.2-beta.22

2 years ago

3.3.2-beta.20

2 years ago

3.3.2-beta.19

2 years ago

3.3.2-beta.18

2 years ago

3.3.2-beta.17

2 years ago

3.3.2-beta.16

2 years ago

3.3.2-beta.14

2 years ago

3.3.2-beta.13

2 years ago

3.3.2-beta.9

2 years ago