Quickstart¶
Eager to get started? This page gives a good introduction to Protean. Follow Installation to set up a project and install Protean first.
In this quickstart, we will create a simple Protean application with SQLITE as the database and Flask as the API framework.
Initialize a Project¶
Let us initialize a new directory for your project. We will call it authentication
.
From the command line, cd into a directory where you’d like to store your code, then run the following command:
$ protean new authentication
This will create a authentication
directory in your current directory.
Here is a quicklook at the directory structure:
A Simple Domain¶
A simple Protean domain looks something like this:
from protean import Domain
domain = Domain(__name__)
Here’s what we did:
First we imported the
protean.Domain
class. An instance of this class will be our domain root to which all elements are attached.Next, we create an instance of this class. The optional argument is the name of the domain’s module or package.
__name__
is a convenient shortcut for this that is appropriate for most cases, so it is the default if no name is specified explicitly.
Define an Aggregate¶
Aggregates are the basic building blocks of the domain. Use the protean.Domain.aggregate()
decorator to bind an Aggregate to the domain.
from protean.field import String
@domain.aggregate
class User:
name = String(max_length=50)
email = String(max_length=255, unique=True)
Define an Application Service¶
Application services expose the domain to the external world. You can create an Application Service with the help of protean.Domain.application_service()
decorator.
@domain.application_service
class SignupService:
@classmethod
def signup(cls, name, email):
user = User(name=name, email=email)
domain.repository_for(User).add(user)
return user
Configure a database¶
By default, a Protean domain is configured with an protean.adapters.repository.MemoryProvider
that manages a dictionary database in memory. This database is handy when you get started with your domain, especially for testing purposes. You can also specify an alternate implementation by overriding the database config. Let’s do that and specify an SQLITE database.
Note that Protean uses SQLAlchemy to access the SQLITE database internally.
domain.config["DATABASES"]["default"] = {
"PROVIDER": "protean.adapters.repository.sqlalchemy.SAProvider",
"DATABASE": "SQLITE",
"DATABASE_URI": "sqlite:///quickstart.db",
}
A database file quickstart.db
will be created in the location you will be running your application from.
Initialize the domain¶
Before you can use the domain, you need to initialize it. Initialize the domain by calling protean.Domain.init()
on the domain instance.
domain.init(traverse=False)
Since all our code is in the same module, we can use traverse=False
. If you have your code spread across multiple modules, you can set traverse=True
to traverse the entire module tree and load all the elements.
Configure Flask¶
Let’s next expose the domain to the external world via APIs with Flask. We accomplish this by activating <TO-LINK> the domain in a function that runs before every request.
We also register a function to run before Flask processes the very first request, in which we set up the database with a table whose structure is auto-generated from the Aggregate definition.
from flask import Flask
app = Flask(__name__)
@app.before_request
def set_context():
context = domain.domain_context()
context.push()
If you want to create the database tables automatically from the structure defined in the domain, you can:
@app.before_first_request
def setup_db():
with domain.domain_context():
for provider in domain.providers_list():
for _, aggregate in domain.registry.aggregates.items():
domain.repository_for(aggregate.cls)._dao
provider._metadata.create_all()
Define a route¶
We are now ready to define API routes for the domain. Let’s create a route that helps us create new users as well as returns a list of all existing users.
@app.route("/users", methods=["GET", "POST"])
def users():
if request.method == "POST":
user = SignupService.signup(request.form['name'], request.form['email'])
return json.dumps(user.to_dict()), 201
else:
users = current_domain.repository_for(User).all()
return json.dumps([user.to_dict() for user in users]), 200
Start the Flask server¶
To run the Flask application, use the flask
command or python -m flask
. The snippet below assumes that your code is saved in a file named quickstart.py
. If it is not, adjust the command accordingly.
$ export FLASK_APP=quickstart
$ flask run
If all is well, you should see a success message at the console along with the URL to access the Flask server.
Access the domain over APIs¶
You can access the APIs once the server is running. We can use HTTPie to fire requests from the console. Let’s first fire a POST
request to create a user.
http -f POST http://localhost:5000/users name=John email=john.doe@example.com
You should see a success message with the user record that was just created.
HTTP/1.0 201 CREATED
Content-Length: 95
Content-Type: text/html; charset=utf-8
Date: Mon, 09 Aug 2021 16:19:31 GMT
Server: Werkzeug/1.0.1 Python/3.9.4
{
"email": "john.doe@example.com",
"id": "41de0f44-9dd0-4ac9-98e3-5e2eca498511",
"name": "John"
}
We can now fire a GET
request to retrieve all users from the database.
http http://127.0.0.1:5000/users
HTTP/1.0 200 OK
Content-Length: 97
Content-Type: text/html; charset=utf-8
Date: Mon, 09 Aug 2021 16:19:36 GMT
Server: Werkzeug/1.0.1 Python/3.9.4
[
{
"email": "john.doe@example.com",
"id": "41de0f44-9dd0-4ac9-98e3-5e2eca498511",
"name": "John"
}
]
That’s it! You have now created a simple Protean domain with SQLITE and Flask and accessed it over the web.