Facade

A frond never imports another frond's code. It asks for the other frond's handler by type — Facade<PostHandler> — and calls its methods. Whether that frond runs in this process or in another one is said by the config, never by this line.

// fronds/mail/handlers/DigestHandler.ts
import type { Facade } from '@fougere/core';
import type PostHandler from '@fronds/blog/handlers/PostHandler.js';

export default class DigestHandler {
  constructor(private posts: Facade<PostHandler>) {}

  /** The titles of the week's digest. */
  async titles(): Promise<string[]> {
    const page = await this.posts.list();

    return page.items.map((post) => post.title);
  }
}

The import is import type: the mail frond holds no code of the blog, only its type.

What the type checks

  • The same arguments. posts.publish(id, user) takes what PostHandler.publish takes, checked by TypeScript.
  • Always a promise. The call may cross a network, so every method answers one.
  • Only operations. A private or protected method is not an operation, and the facade does not offer it.
  • No placement. The same type is the local facade or a stand-in for a remote process. Adding blog: { remote: '…' } to fronds: changes nothing here.

A call through a facade runs what a call from outside runs: the checks of the input, the links around the calls, and the answer checked on its way out.

Pass everything, the user included

A collector reads a request, and code calling a facade has none. It already holds the user, so it passes it like any other argument:

await this.posts.publish(id, user);

Leave it out and the operation receives no user. For the same reason a presenter adds nothing to what a facade answers: you get the rows the handler returned. Collectors and presenters belong to the doors — REST, GraphQL, a page — which receive a request from outside.

Not the handler itself

Asking another frond for its class — constructor(private posts: PostHandler) — would skip the checks, the links and the placement, and break the day the blog moves. The boot warns cross-frond-dependency and names the facade to ask for instead. Inside its own frond, a class asking for PostHandler gets the instance its facade calls.

When nobody should be named

A facade names one recipient. When the blog should not know who cares that a post was published, it announces a fact instead, and whoever accepts it is called. When two writes must land together, see Writes that stand or fall as one.

fougere graph prints the fronds and which one calls which.

Put it to work

Built with Fougere and Nuxt, hosted on GitHub Pages.