Andrea Bizzotto πŸ’™ Profile picture
Feb 10 β€’ 14 tweets β€’ 4 min read
How do we apply Domain-Driven Design to exception handling?

I've been experimenting with some techniques and would love your feedback about this thread. 🧡

First of all, let's establish some goals: πŸ‘‡
To define all the domain-specific exceptions, sealed unions come to hand.

Here's how we may generate Auth and Database exception types using Freezed:
Then we can define one *global* function that does two things:

- run the Future<T> that is given as an argument inside a try/catch block
- map all 3rd-party exceptions to our own exception types (and throw them)
Inside our repositories, we can wrap each Future-based API with the runCatchingExceptions() function.

As a result, if the code throws, it will throw any of our own exception types.
And when we implement the service classes in the application layer, we can:

- run any business logic using our repositores
- catch our exceptions
- return (not throw) them using a Result type that can contain Error or Success

This is also a good place to *log* exceptions
Nearly there!

If we use StateNotifier to control our widget's state, we can:

- call the method in the service class
- get the result and map it to an error/success state
And finally, in the widgets we can watch the state and listen for errors (I have already covered this before):

codewithandrea.com/articles/loadi…
The end result?

- we catch all exceptions from the *outside world* by wrapping all async repository methods with the runCatchingExceptions() function
- the rest of our app only deals with exceptions that belong to the domain model

(continued)
- (optional) service classes will catch exceptions and return a Result<Error, Success> type
- (optional) controller classes will use Result type and easily map any errors to user-facing messages

This encourages us to define every possible error state and show it in the UI.
One drawback of this approach is that we now rely on Freezed code generation to define sealed unions for all error types.

Depending on the project, code generation can be slow - though this can be somewhat mitigated:

codewithandrea.com/tips/speed-up-…
So far, what I presented is a WIP and there are some things I want to figure out.

- How to apply the same approach to Streams rather than Futures?
- Should we map Stream<T> to Stream<Result<Error, T>> everywhere? Seems complicated in practice.

(continued)
Another issue is that having more than one exception type (e.g. AuthException, DatabaseException) can make it hard to map errors across the different layers.

Maybe it would be best to define just ONE exception type with all possible errors (it's a sealed union after all).
I want experiment more with this topic and find a robust solution that works well without adding too much mental overhead.

Did you like this thread? Anything you would have done differently?

Let me know in the comments. πŸ‘‡
There is so much to cover around app architecture, error handling, and domain-driven design.

And I'll try my best to make it justice in my upcoming course.

If that sounds interesting, sign up to get a *big* discount when I open the pre-sale:

codewithandrea.com/courses/comple…

β€’ β€’ β€’

Missing some Tweet in this thread? You can try to force a refresh
γ€€

Keep Current with Andrea Bizzotto πŸ’™

Andrea Bizzotto πŸ’™ Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!

PDF

Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

More from @biz84

Feb 9
Domain-Driven Design helps us write complex software.

At the heart of it lies the *domain model*, which is a conceptual model that incorporates both behavior and data.

Sounds complicated?

Let's explain with an example. 🧡
Suppose we want to build an eCommerce app.

To better understand this domain, we need to figure out:

- the entities we care about and their relationships
- the behaviour (or business logic) for manipulating them

The end result may look like this:
In Dart, we can represent each entity as a simple model class.

As we define this, it helps to think about all the properties that we need to show in the UI:
Read 7 tweets
Jan 26
The default way of accessing localized strings inside your widgets is to use the `AppLocalizations` class.

However, using the `!` operator everywhere is not good practice.

And it would be nice to have some more "lightweight" syntax.

A thread. 🧡 Image
One issue we have is that `AppLocalizations.of(context)` returns a *nullable* object.

But at runtime this will never be null, as long as we call it inside a *descendant* of MaterialApp (which is always the case).

So what can we do? Image
Dart extensions to the rescue!

We can define a LocalizedBuildContext extension with a `loc` getter variable defined like so: Image
Read 6 tweets
Jan 21
When building non-trivial apps, following a *structured workflow* can bring great benefits and avoid many pitfalls.

Rather than diving straight into the code, here's a 6 step workflow that you can follow.

Thread. 🧡
1. Design your app (part 1)

Design all the screens that will be in your app.

You can use Sketch, Figma, Adobe XD, or even draw them by hand.

This will help you think about the most logical UI flows before diving into the code.

Here's an example from my website: πŸ‘‡
1. Design your app (part 2)

This is also a great chance to define stylesheets including a color palette, typography, paddings etc.

This will:

- make your designs much more consistent
- make your app look much nicer
- save you time down the line
Read 12 tweets
Jan 7
"Program to interfaces, not implementations" is a very important concept in software design.

It is used to decouple your code from implementation-specific details.

A good use case for this is when creating repositories that connect to external data sources:
Once you have an interface (abstract class), you can *implement* it with a concrete implementation.

You can even create a "fake", which can be very useful in your tests.
During app startup, you can initialize your repository with a *concrete* instance (using a service locator or any other dependency injection system).

And the rest of your app can just access the repository using the base abstract class.
Read 4 tweets
Dec 31, 2021
Here's a thread about my 2021 in numbers as a content creator making Flutter tutorials and courses. 🧡

Total Revenue: $69,532

Here's a breakdown by month. πŸ‘‡
Here's a pie chart about revenue by income stream.

80% of total revenue came from sales on Udemy and Teachable.
This year I had $20,660 in expenses.

This is a large number primarily due to outsourcing costs (design & development) on my site.
Read 11 tweets
Oct 12, 2021
How do you navigate programmatically between tabs like in Flutter?

Let's figure it out. 🧡
First of all, we need:

- a TabBar with three tabs
- a TabController to control the selected tab
Then, let's add a TabBarView to contain all the views (pages).

Our custom views have an "onNext" callback that we can use to change the index of the TabController (and update the page).
Read 8 tweets

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Don't want to be a Premium member but still want to support us?

Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal

Or Donate anonymously using crypto!

Ethereum

0xfe58350B80634f60Fa6Dc149a72b4DFbc17D341E copy

Bitcoin

3ATGMxNzCUFzxpMCHL5sWSt4DVtS8UqXpi copy

Thank you for your support!

:(