FILAR

Application framework for Effect TypeScript.
Define your application around domain operations.
Expose them through honest boundaries.

Documentation

Simple

const PayInvoice = Filar.operation({
  name: "PayInvoice",
  input: PayInvoiceInput,
  output: Payment,
  // This is the application logic.
  run: ({ invoiceId }) => Payments.pay(invoiceId),
})

const app = Filar.make()
  // Reuse the operation contract directly.
  .expose(Http.post("/invoices/pay", PayInvoice))
  // The same operation can have other boundaries.
  .expose(Cli.command("invoice pay", PayInvoice))

Advanced

const PayInvoice = Filar.operation({
  name: "PayInvoice",
  input: PayInvoiceInput,
  output: Payment,
  errors: [InvoiceNotFound, PaymentRejected],
  run: ({ invoiceId, paymentMethodId }) =>
    Payments.pay(invoiceId, paymentMethodId),
})

const app = Filar.make().expose(
  Http.post("/invoices/:invoiceId/pay", PayInvoice, {
    input: {
      // The HTTP contract can have its own shape.
      path: Schema.Struct({
        invoiceId: Schema.String,
      }),
      body: Schema.Struct({
        paymentMethod: Schema.String,
      }),
      // Map the transport input into the operation input.
      toOperation: ({ path, body }) => ({
        invoiceId: InvoiceId.make(path.invoiceId),
        paymentMethodId: PaymentMethodId.make(body.paymentMethod),
      }),
    },
    output: {
      schema: Schema.Struct({
        id: Schema.String,
        status: Schema.Literal("paid"),
      }),
      // Decide exactly what crosses this boundary.
      fromOperation: (payment) => ({
        id: payment.id,
        status: payment.status,
      }),
    },
    errors: {
      // Domain errors get an HTTP representation here.
      InvoiceNotFound: Http.error(404),
      PaymentRejected: Http.error(422),
    },
  }),
)

Values

Paired with:

Anti-goals


Start with the documentation.