🚀 Building a Fullstack App with dart_frog and Flutter in a Monorepo - Part 2
Tags: dart, flutter, fullstack, dartfrog
Published: 2022-12-22 04:53:27

---
title: 🚀 Building a Fullstack App with dart_frog and Flutter in a Monorepo - Part 2
published: true
date: 2022-12-22 04:41:34 UTC
tags: dart, flutter, fullstack, dartfrog
series: 🚀 Building a Fullstack App with dart_frog and Flutter in a Monorepo
canonical_url: https://saileshdahal.com.np/building-a-fullstack-app-with-dartfrog-and-flutter-in-a-monorepo-part-2
---

In the first part of this article, we set up [melos](https://docs.page/invertase/melos) as a tool for managing monorepo projects and installed [dart\_frog](https://pub.dev/packages/dart_frog) as a web framework for building server-side apps with Dart. We also created a new Dart Frog project, which included adding the `dart_frog` dependency on the project's `pubspec.yaml` file and creating the necessary files and directories.

We will create models, data sources, repositories, and failure classes as a separate Dart package in part 2.

To do this, we will be using [very\_good\_cli](https://cli.vgv.dev/).

# Setting up Models, Repositories, and Data Services 💻

## Installing [very\_good\_cli](https://cli.vgv.dev/)

> **very\_good\_cli** , a Command-Line Interface that provides helpful commands and templates for generating various types of projects, including Flutter apps, Flame games, Flutter packages, Dart packages, federated plugins, and Dart CLI projects.

**Very Good CLI** makes it easy to create and set up these types of projects with a single command, so let's get started!

To install **Very Good CLI** , open a terminal and enter the following command:

```
dart pub global activate very_good_cli

```

To confirm that Very Good CLI has been installed correctly, you can enter the following command in a terminal `very_good --version` 🚀

## Creating `typedefs`

We will have our **typedefs** , which will be shared between the backend and frontend of our project. To create a new Dart package for these typedefs, we can use the following command:

```
very_good create -t dart_pkg typedefs

```

This will generate a new dart package `typedefs`. Here `-t dart_pkg` means we are creating a new dart package.

Once the `typedefs` package has been created and the necessary files and directories have been generated, you can navigate to `typedefs/lib/src/typedefs.dart` to create a new typedef.

In this file, you can create a new **typedef** by adding the following code:

```
/// Primary key type for a Todo.
typedef TodoId = int;

```

This will create a new typedef called `TodoId`, an alias for the type `int`. This typedef can then be used throughout the backend and frontend of your project to represent an identifier for a to-do item. Once you are done, make sure to export the file from `lib/typedefs.dart`

```
library typedefs;
export 'src/typedefs.dart';

```

We will add additional shared **typedefs** to this file as needed. [**GitHub Link**](https://github.com/saileshbro/full_stack_todo_dart/tree/main/typedefs)

## Creating `models`

Similarly, we will have a separate package to house shared data models.

1. `CreateTodoDto` This will be used to send and receive payload for creating a new todo.

2. `UpdateTodoDto` This will be used to send and receive payload for updating an existing todo.

3. `Todo` This is the representation of the `Todo` entity.

To create a separate package for models you can use the `very_good` CLI:

```
very_good create -t dart_pkg models

```

Once the package has been created, we will install [freezed](https://pub.dev/packages/freezed) for **JSON serialization** and **value equality** , as this library provides helpful tools for these tasks. We will use [json\_serializable](https://pub.dev/packages/json_serializable) for JSON serialization. To install [freezed](https://pub.dev/packages/freezed) and [freezed\_annotation](https://pub.dev/packages/freezed_annotation), open your terminal inside the `models` package and use the command:

```
dart pub add freezed json_serializable build_runner -d
dart pub add freezed_annotation json_annotation

```

Inside `models/lib` we will create files in the given order. We have created a parent folder for each model to house the generated codes so that it will look cleaner.

```
.
 models.dart
 src
     create_todo_dto
        create_todo_dto.dart
     todo
        todo.dart
     update_todo_dto
         update_todo_dto.dart

```

Now, in `todo.dart` we will use [freezed](https://pub.dev/packages/freezed) package to create a data class.

> [freezed](https://pub.dev/packages/freezed) package is a code generation tool that allows you to create immutable classes with concise, boilerplate-free syntax. It uses the `@freezed` annotation to generate a number of useful methods and operators for your class, such as `==`, `hashCode`, and `copyWith`.

```
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:typedefs/typedefs.dart';

part 'todo.freezed.dart';
part 'todo.g.dart';

@freezed
class Todo with _$Todo {
  factory Todo({
    required TodoId id,
    required String title,
    @Default('') String description,
    @Default(false) bool? completed,
    required DateTime createdAt,
    DateTime? updatedAt,
  }) = _Todo;

  factory Todo.fromJson(Map<String, dynamic> json) => _$TodoFromJson(json);
}

```

Since `TodoId` is in a separate package, we will have to add it in the `models/pubspec.yaml` file as a dependency. After adding the `pubspec.yaml` should look like this.

```
name: models
description: A Very Good Project created by Very Good CLI.
version: 0.1.0+1
publish_to: none

environment:
  sdk: ">=2.18.0 <3.0.0"

dependencies:
  freezed_annotation: ^2.2.0
  json_annotation: ^4.7.0
  typedefs:
    path: ../typedefs

dev_dependencies:
  build_runner: ^2.3.3
  freezed: ^2.3.2
  json_serializable: ^6.5.4
  lints: ^2.0.0
  mocktail: ^0.3.0
  test: ^1.19.2
  very_good_analysis: ^3.1.0

```

Similarly, we will create `create_todo_dto.dart` for `CreateDotoDto` class.

```
import 'package:freezed_annotation/freezed_annotation.dart';

part 'create_todo_dto.freezed.dart';
part 'create_todo_dto.g.dart';

@freezed
class CreateTodoDto with _$CreateTodoDto {
  factory CreateTodoDto({
    required String title,
    required String description,
  }) = _CreateTodoDto;

  factory CreateTodoDto.fromJson(Map<String, dynamic> json) =>
      _$CreateTodoDtoFromJson(json);
}

```

Similarly, we will create `update_todo_dto.dart` for `UpdateTodoDto` class.

```
import 'package:freezed_annotation/freezed_annotation.dart';

part 'update_todo_dto.freezed.dart';
part 'update_todo_dto.g.dart';

@freezed
class UpdateTodoDto with _$UpdateTodoDto {
  factory UpdateTodoDto({
    String? title,
    String? description,
    bool? completed,
  }) = _UpdateTodoDto;

  factory UpdateTodoDto.fromJson(Map<String, dynamic> json) =>
      _$UpdateTodoDtoFromJson(json);
}

```

Once this is done, we run [build\_runner](https://pub.dev/packages/build_runner) and generate the necessary code. To generate the missing code, you can run the following command:

```
dart pub run build_runner build --delete-conflicting-outputs

```

This will generate `*.g.dart` and `*.freezed.dart` files. Once this is done, you will have files inside `models/lib` in the given order.

```
.
 models.dart
 src
     create_todo_dto
        create_todo_dto.dart
        create_todo_dto.freezed.dart
        create_todo_dto.g.dart
     todo
        todo.dart
        todo.freezed.dart
        todo.g.dart
     update_todo_dto
         update_todo_dto.dart
         update_todo_dto.freezed.dart
         update_todo_dto.g.dart

```

Once you are done creating the models, be sure to export them from `lib/models.dart`

```
library models;

export 'src/create_todo_dto/create_todo_dto.dart';
export 'src/todo/todo.dart';
export 'src/update_todo_dto/update_todo_dto.dart';

```

You can find the [GitHub link for models here](https://github.com/saileshbro/full_stack_todo_dart/tree/main/models).

## Creating `failures`

In the next step, we will create a separate package to handle failures in our application. This package will contain all the failures that may occur, such as `NetworkFailure` or `ServerFailure`. Whenever we need to handle an error, we will encapsulate the value or error in a union type and return it from a repository. For example, when we make an API call to retrieve data, we may receive either the requested data or an error. By encapsulating this in an `Either` type, we can effectively handle and manage failures in our app.

> This is inspired by [ReSo Coder's Clean Architecture](https://resocoder.com/flutter-clean-architecture-tdd/) tutorial.

To create the failures package:

```
very_good create -t dart_pkg failures

```

This will create a new package in `failures` directory. We will use `freezed` and `json_serializable` libraries for data serialization here as well.

```
dart pub add freezed json_serializable build_runner -d
dart pub add freezed_annotation json_annotation

```

To begin, we will create a `Failure` class inside the `lib/src/failure.dart` directory. This class will be the base for all failure types and will handle and manage our app's failures.

```
abstract class Failure {
  String get message;
}

```

We can then create specific failure classes, such as `NetworkFailure`, to return to the caller whenever there is a network exception. For instance, if we have a `TodosRemoteDataSource` that makes a call to a backend and encounters a HTTP exception, the data source can throw a `NetworkException` which will be caught by a repository `TodoRepository`. The repository will then encapsulate the error in a union type and return either the requested data or the failure to the caller. This allows us to effectively handle and manage failures in our app.

To create a `NetworkFailure` class, we will create a new file in the `src` directory called `network_failure/network_failure.dart`.

```
import 'package:failures/failures.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

part 'network_failure.freezed.dart';
part 'network_failure.g.dart';

@freezed
class NetworkFailure extends Failure with _$NetworkFailure {
  const factory NetworkFailure({
    required String message,
    required int code,
    @Default([]) List<String> errors,
  }) = _NetworkFailure;

  factory NetworkFailure.fromJson(Map<String, dynamic> json) =>
      _$NetworkFailureFromJson(json);
}

```

Once you have finished creating the `NetworkFailure` class, you can run the `build_runner` command to generate the necessary files for your project.

Make sure to export the failure class from `failures/lib/failures.dart`

```
library failures;

export 'src/failure.dart';
export 'src/network_failure/network_failure.dart';

```

You can view [the code from here.](https://github.com/saileshbro/full_stack_todo_dart/tree/main/failures)

## Creating `data_source`

We will create a new package with `very_good_cli` for our data source.

```
very_good create -t dart_pkg data_source

```

Once the package is created, we can create an abstract data source contract.

```
import 'package:models/models.dart';
import 'package:typedefs/typedefs.dart';

abstract class TodoDataSource {
  Future<List<Todo>> getAllTodo();

  Future<Todo> getTodoById(TodoId id);

  Future<Todo> createTodo(CreateTodoDto todo);

  Future<Todo> updateTodo({
    required TodoId id,
    required UpdateTodoDto todo,
  });

  Future<void> deleteTodoById(TodoId id);
}

```

You can import the missing packages in `pubspec.yaml`. After importing, it should look like this.

```
name: data_source
description: A Very Good Project created by Very Good CLI.
version: 0.1.0+1
publish_to: none

environment:
  sdk: ">=2.18.0 <3.0.0"
dependencies:
  models:
    path: ../models
  typedefs:
    path: ../typedefs

dev_dependencies:
  mocktail: ^0.3.0
  test: ^1.19.2
  very_good_analysis: ^3.1.0

```

The `TodoDataSource` class will be used by both the frontend and backend of our app to access and manage data related to to-dos. This class will **throw known exceptions** that can be handled in the `TodoRepository`. To manage these exceptions, we will create a separate `exceptions` package where we can **register all the exceptions** used in our app. The `TodoRepository` will then be **responsible for handling these exceptions** , allowing us to effectively manage and handle errors in our application.

Once you are done adding the data source, make sure to export it in `data_sources/data_sources.dart`

```
library data_source;

export 'src/todo_data_source.dart';

```

You can view the code [for the data\_source from GitHub.](https://github.com/saileshbro/full_stack_todo_dart/tree/main/data_source)

## Creating `repository`

We will make use of the `TodoDataSource` class to access and manage data related to to-dos. We will also catch any exceptions thrown by the `TodoDataSource` and serialize them as failures. `TodoRepository` will then return the failure or the requested data to the caller.

To create the `TodoRepository` class, we will first navigate to the root directory of our project and create a new package using the `very_good` CLI:

```
very_good create -t dart_pkg repository

```

Once we are done, we will add all the dependencies in `pubspec.yaml` and run `dart pub get` to get all the packages.

```
dependencies:
  either_dart: ^0.3.0
  models:
    path: ../models
  failures:
    path: ../failures
  typedefs:
    path: ../typedefs
  data_source:
    path: ../data_source

```

To create an abstract `TodoRepository` class, we will navigate to our project's `lib/src` directory and create a new file called `todo_repository.dart`. Inside this file, we will make the `TodoRepository` class and add the necessary abstract methods that will be used to manage and access data related to to-dos.

```
import 'package:either_dart/either.dart';
import 'package:failures/failures.dart';
import 'package:models/models.dart';
import 'package:typedefs/typedefs.dart';

abstract class TodoRepository {
  Future<Either<Failure, List<Todo>>> getTodos();

  Future<Either<Failure, Todo>> getTodoById(TodoId id);

  Future<Either<Failure, Todo>> createTodo(CreateTodoDto createTodoDto);

  Future<Either<Failure, Todo>> updateTodo({
    required TodoId id,
    required UpdateTodoDto updateTodoDto,
  });

  Future<Either<Failure, void>> deleteTodo(TodoId id);
}

```

We are using [either\_dart](https://pub.dev/packages/either_dart) to encapsulate the failure and data in the same place.

> [either\_dart](https://pub.dev/packages/either_dart) is the error handling and railway-oriented programming library is a Dart library that supports async "map" and async "then" functions for working with asynchronous computations and handling errors with `Future<Either>` values.

After you are done, make sure to export it in `src/repository.dart`

```
library repository;

export 'src/todo_repository.dart';

```

You can find the [code for this on GitHub](https://github.com/saileshbro/full_stack_todo_dart/tree/main/repository).

Damn! that was a lot of code and a lot of writing 😮💨

🎉 In Part 3, we're going to get started on the implementation of our repository! 💻 We'll begin by setting up the backend and establishing database connections. 🔌 It's going to be a lot of fun and a great opportunity to get hands-on with the code. So stay tuned! 😎