Design Patterns for Failure

I'm wondering if there is a good design pattern to follow that allows failures to be collapsed based on separation of concerns (for example, when implementing a service).

For example, when I implement FooService, it encounter specific errors such as BarError and BazError, which could be dependent on the specific implementation. From an interface perspective, it seems sensible to collapse these errors into a FooError (perhaps with semantics that are relevant to the FooService, e.g. FooValidationError or FooAuthError). In essence, FooService should only expose failure types that make sense, and not the implementation specifics.

At the moment I am creating a tagged error using Data.TaggedError that contains a cause: { _tag: string } field which carries the underlying failure (useful for debug purposes) which seems broad enough to capture all underlying known failures. I've not been able to find a specific type that represents failures, other than that they are tagged.

I'm encountering this issue when using AWS SDK (via @effect-aws) where the various clients can produce a number of failure types, but for the service that implements them, there isn't a reason to know about the specifics, rather the semantics such as whether the error is retry-able etc.

So when I make a client call, I have some sort of .catch which maps to the services more generic error type:

client.action(/*...*/).catchAll(cause => new FooError({ cause })


which allows the consumer of FooService to then catch on FooError tags, rather than the underlying implementation specific failure.
Was this page helpful?