2. Generate An App

Tight has been designed to aid in fast and repeatable development of microservices. As such, Tight provides a suite of commands that generate files and directories that are common to all projects. The very first generate command that we are going to explore is tight generate app.

Generate Project Directory

Navigate to the location where you want to create your app. I like to keep my code projects in the Development directory in my home location, so I’ll head that way.

$ cd ~/Development

Now I’m going to use tight generate app to generate a new project. Simply provide a name as the command’s only argument:

$ tight generate app tight_app

Your app has been generated! To verify, you can list the contents of the tight_app directory.

drwxr-xr-x  12 user  group   408B Jan  2 14:56 .
drwxr-xr-x@ 24 user  group   816B Jan  2 14:56 ..
-rw-r--r--   1 user  group   143B Dec 25 17:07 .gitignore
drwxr-xr-x   8 user  group   272B Jan  2 14:56 app
-rw-r--r--   1 user  group    76B Dec 25 16:38 app_index.py
-rw-r--r--   1 user  group   476B Jan  2 14:56 conftest.py
-rw-r--r--   1 user  group    56B Dec 26 02:28 env.dist.yml
-rw-r--r--   1 user  group    60B Dec 17 18:03 requirements-vendor.txt
-rw-r--r--   1 user  group    40B Dec 17 18:05 requirements.txt
drwxr-xr-x   4 user  group   136B Dec 23 12:39 schemas
drwxr-xr-x   4 user  group   136B Dec 23 11:22 tests
-rw-r--r--   1 user  group    54B Jan  2 14:56 tight.yml

The Anatomy of a Default App

app/

The app directory, not surprisingly, contains your application’s runtime logic. This directory organizes your application’s function code along with dependencies and share libraries.

List the contents of the directory to see what was created:

$ ls -la app

drwxr-xr-x   8 user  group   272B Jan  2 14:56 .
drwxr-xr-x  13 user  group   442B Jan  2 14:57 ..
-rw-r--r--   1 user  group   339B Dec 23 11:22 __init__.py
drwxr-xr-x   3 user  group   102B Dec 17 17:25 functions
drwxr-xr-x   3 user  group   102B Dec 23 11:22 lib
drwxr-xr-x   3 user  group   102B Dec 23 10:46 models
drwxr-xr-x   3 user  group   102B Dec 23 10:46 serializers
drwxr-xr-x   3 user  group   102B Jan  2 14:56 vendored

app/__init__.py

The app directory’s __init__.py file augments the current environment so that application dependencies are discoverable for import.

The generator currently produces the following file:

import sys, os
here = os.path.dirname(os.path.realpath(__file__))
sys.path = [os.path.join(here, "./vendored")] + sys.path
sys.path = [os.path.join(here, "./lib")] + sys.path
sys.path = [os.path.join(here, "./models")] + sys.path
sys.path = [os.path.join(here, "./serializers")] + sys.path
sys.path = [os.path.join(here, "../")] + sys.path

These statements are what allow function code to import packages in the vendored, lib, models, and serializers directories without having to use relative imports. If you do not wish to alter the environment’s import path, the contents of this file can be removed. However, do not remove the file completely.

app/functions/

The functions directory is where your application’s business logic lives. Later in the tutorial, when we start creating functions, we’ll explain the naming and file conventions that should be followed within the functions directory.

For now all you need to know is that the directory is identified as a package, since it has a blank __init__.py file.

app/lib/

The lib directory is where you should keep modules and packages that are shared across functions but that aren’t installable via pip.

app/models/

The models directory is where the domain objects that your application manipulates should be stored. Like the function directory, files placed in the model directory should conform to Tight’s conventions.

Modules in this directory should define a single model and the name of the model and file should be the same.

Imagine creating an Account model:

$  ls -la app/models
drwxr-xr-x  4 user  group   136B Jan  2 15:27 .
drwxr-xr-x  8 user  group   272B Jan  2 14:56 ..
-rw-r--r--  1 user  group    80B Jan  2 15:27 Account.py
-rw-r--r--  1 user  group   460B Dec 23 10:46 __init__.py

$  less Account.py

def Account(id):
    """ Account factory """
    return {
        'id': id
    }

app/models/__init__.py

Unlike the other directories that get created inside of app, the __init__.py file inside of models is not empty. This file will loop through the files in the directory and automatically import the models that are defined. So long as the convention described above is followed, you will be able to succinctly import models into function modules.

The Acccount model defined above would be imported like so:

from Account import Account

app/serializers/

Tight encourages you to maintain serialization logic separately from model modules. As such, Tight provides a location where serializers can be kept.

app/vendored/

The vendored directory is where your _application’s_ pip packages are installed.

app_index.py

This is the module that is used to route Lambda events to the correct function.

from app.vendored.tight.providers.aws.lambda_app import app as app
app.run()

The function tight.providers.aws.lambda_app.run collects functions from app/functions and sets attributes on the module for each function found. This means that when you go to configure your Lambda function within AWS, you can refer to module attributes that mirror functions:

app_index.a_function_in_your_app

This will call the handler function on the module located at app/functions/a_function_in_your_app/handler.py.

conftest.py

conftest.py provides the minimum confugration needed to run function tests. The module also imports the tight.core.test_helpers module, which exposes test fixtures and other goodies to help you start writing tests right away.

env.dist.yml

This file contains the default values for the environment variables that your application expects. You shouldn’t store sensitive or environment specific values here. By default, this file specifies that the environment variables, CI and STAGE are expected:

# Define environment variables here
CI: False
STAGE: dev

As your application evolves remember to update this file with new names:

# Define environment variables here
CI: False
STAGE: dev
SOME_API_KEY: <optionally provide a default value>

requirements-vendor.txt

Specify pip package dependencies, which will be installed to app/vendored by default.

requirements.txt

Specify pip package dependencies that are to be installed to the virtual environment. Typically this is where you’ll define dependencies that are required for developing and testing your app.

Dependencies specified here will not be packaged with your application artifact.

schemas/

This directory will contain CloudFormation compatible DynamoDb schemas, which can be auto-generated from model definitions.

tests/

Tight really wants to help you develop your application test-first. It would be a tragedy and an embarrasment if Tight didn’t provide you a place to store your tests. Once we stater generating functions, we’ll dive deeper into the structure of this directory.

tight.yml

tight.yml is this Tight app’s configuration file. There’s nothing too fancy about it and throughout the course of the tutorial, you’ll rarely have to modify it. Just be aware that it exists and that it is the location from which the command line tool pulls the application name. Every time a tight command is run, this file is discovered and parsed and the values it defines are used throughout various commands.

Install Dependencies

Now that our application structure has been scaffolded, it’s time to install our dependencies. First we’ll install our virtual environment depedencies and then we’ll install our application specific dependencies.

Environment Dependencies

Install environment dependencies just as you would for any other virtual environment.

$ pip install -r requirements.txt

App Dependencies

Application dependencies are also installed via pip but we need to be sure that they get installed to the correct location so that when our application artifact is deployed, any third-party libraries that your application relies on are available. To install your application dependencies, navigate to your project root and run tight pip install --requirements:

$ tight pip install --requirements

When you run this command, you’ll notice that at the very end of the run you are notified that the boto3 and botocore packages have been removed from the app/vendored directory. This is because both packages are supplied by the AWS Lamba execution environment. Generally, you shouldn’t include these packages in your application artifact.

Conclusion

By now, you have scaffolded your first Tight app and should have a basic grasp of the purpose and reason for the auto-generated files and directories.

You also learned how to install your application and virtual environment dependencies.

Continue reading to learn about how Tight helps you initialize and manage application environment variables.