Building a Social Network: Part II
Creating a Dart package with common data models
Introduction
In the previous article of this series, we looked at the underlying SQL schema for the social network demo with built-in security and privacy features. In this article, we will examine the next phase in the bottom-up design of this application: the data model library.
By defining models in a common library, integrity can be maintained between the data models and the underlying DB schema in one convenient location, allowing any number of components in the future to be built, with compile-time type safety for increased reliability.
The data models reflect the schema as expected, and provide convenient methods for JSON serialization and other features. An additional layer of hashing is used to avoid sending passwords in readable plaintext.
Dart was chosen because it has a powerful feature set and is the development language for apps in Flutter, which is great for rapidly building cross-platform client applications with high-performance UX.
The project source code for this article series is available here on GitHub.
Package Definition
The Dart package for the core library is defined in core/pubspec.yaml:
This creates a simple package that can be easily imported into the API, client applications, utility and maintenance functions, or any other components of the social network, by adding the core
package as a dependency.
Core
The main export source file in the project is core/lib/core.dart:
This file defines the core
library and exports data models and utility classes to be used by other parts of the software, such as the authentication request model, data models, and a password hash utility.
Serializable
The data model base class is in core/lib/types/serializable.dart:
Data models extend this Serializable
class, with each implementing fromMap and toJson for serialization. This allows system components to operate on serializable objects in a functional manner, without requiring the knowledge of each type’s properties at compile time or runtime.
AuthRequest
Next let’s check out core/lib/models/auth-request.dart:
This class provides a convenient wrapper for making an authentication request, with properties for email and hashed password, along with a create
method for creating a new request, and implementations for fromMap
and toJson
to allow the request to be serialized for network transmission.
This simplifies future development by providing a common interface that is understood by the API and all supported client applications, which improves everything from developer experience to security while reducing the amount of code that has to be written in the future. Errors can be found at compile time instead of runtime, and highly robust testing and verification procedures can be developed to ensure product quality is always achieved.
UserImage
Next up is core/lib/models/image.dart:
An image is either a JPG, PNG, or GIF file, and has a unique ID and a flag to indicate whether or not it is currently set as a profile photo. The ext
accessor returns a typical filename string that can be used for storing the image data in a local file, a cloud storage service, or other location.
User
The user classes are located in core/lib/models/user.dart:
This file contains a baseUser
class along with AuthenticatedUser
and NewUser
for handling authentication and creating new user accounts. Using a common set of classes in this way minimizes the potential for errors within authentication and account creation code.
Post
The common class for post data is in core/lib/models/post.dart:
The Post
class represents a social network post with id, associated user, content, and a fromJSON
to create an instance of Post
from a database record. The class NewPost
defines the required parameters for creating a new post and will be used by client applications to post content using the API.
Room
The chat room data model is in core/lib/models/room.dart:
The Room and NewRoom classes will be used to create, list, and connect to chat rooms. The format is the same as the other data model classes.
Testing
The core
package includes a basic framework for TDD along with a test model class to verify that serialization works as expected. The test model is located in core/lib/models/post.dart:
The TestModel
class extends Serializable
with a few properties and methods for testing basic serialization of primitive types and checking equality with a custom implementation of the ==
equality operator.
The test itself is located in core/test/test-runner.dart:
Serialization is tested by creating a TestModel from scratch, then another from the properties of the first one, another using the data
accessor of the previous, and another created from passing the previous one through JSON serialization methods.
Conclusion
This package serves as a common library for the API and UI to build upon for handling data as it passes in and out of the database, in a way that ensures compatibility and reliability both today and in future maintenance.
Coming soon is Part III in this series, in which this package is imported and used within a REST API to implement endpoints for creating accounts, adding followers, and posting content (including images).
Thanks for reading!