File Structure - NFSandbox/sh_trade_backend GitHub Wiki

This article will introduce the basic file structure of this project.

  • endpoints All files about FastAPI endpoints functions and routers.
  • schemes All data models and schemas should be defined here.
    • sql.py Specially, this file is used to store all ORM class definitions and configurations.
  • provider Provide functionalities used by endpoints. For example perform database query, update data etc.
    • database.py Provide dependencies used by all providers related to database. For example session_maker()

Endpoint

The endpoints directory only takes charges in:

  • Data Validation
  • Basic Authentication (By using some depencency with auth feature)
  • Expose funcionality

The business logic itself is not implemented in the endpoint functions. Instead, we move that part into the functions in provider. So generally, an endpoint function will need to call some other functions in provider to perform operations or retrieve data.

Using get all user items as example:

(endpoints) get_user_item(): Get current user from cookies
(endpoints) get_user_item(): Check all parameters are valid
(endpoints) get_user_item(): Check user is not banned
(endpoints) get_user_item(): Call get_items_from_user() in provider

(provider)  get_items_from_user(): Execute database query, remove soft deleted result
(provider)  get_items_from_user(): Return result

(endpoints) get_user_item(): validate return value, return to user

Provider

provider directory is one of the functionality core directory of this project. All database operations and any other possible feature provided by this API is finally implemented here in this directory.

Providers usually work with endpoint functions. For more info, check out Endpoints part.

Double Layer Provider

All functionalities are implemented in Providers, and one provider may depends on functions in other providers, which may lead to Circular Dependencies in Python.

In order to prevent Circular Dependencies in providers, we need to elevate the Core Functions into core layer of the providers, which lies in provider.[part].core

Here, Core Functions means the functions which may be used by other providers. For example:

For example:

  • provider.user.get_user_by_id()
    • Should be elevated to provider.user.core.user.get_user_by_id()

In this case, the file structure would be:

  • provider/
    • [part]/
      • __init__.py
      • [part].py
      • core.py
    • [other_part]/

Also, the [part].core module could be imported in [part] package:

# in [part].__init__.py
from .core import *
from .[part] import *

By doing this, we could directly use functions in core while import [part] package as a whole:

from provider import [part]

[part].some_function_in_part_core()

Check out the diagram below for a better understanding of this double-layer providers structure.

Double Layer Providers


When using core functions in other providers, we need to directly import the provider.[part].core package instead of provider.[part], for example:

# do NOT do this
from provider.user import get_user_by_id
# instead, do this
from provider.user.core import get_user_by_id