Skip to content

Instantly share code, notes, and snippets.

@amcgregor
Last active August 9, 2023 17:46
Show Gist options
  • Save amcgregor/d88b2cdb456008c35fe23faa6edff9df to your computer and use it in GitHub Desktop.
Save amcgregor/d88b2cdb456008c35fe23faa6edff9df to your computer and use it in GitHub Desktop.
An example web application on-disk structure, and the general template I follow for my WebCore applications, both personal and professional. Also applicable to Django, Flask, or almost any other project that can be broken up modularly. — https://github.com/marrow/webcore#what-is-webcore
# Project root. Cloned into /Volumes/<org>/<project> and symlinked to `~/Projects/<workspace>/src` if isolated as a
# distinct project, or `~/Projects/<workspace>/<project>` if part of a collective work or business space alongside
# others.
# Project Metadata
.gitignore # From template.
.pre-commit-config.yaml # https://pre-commit.com
.travis.yml # Test runner configuration.
MANIFEST.in # Redistributable package inclusions.
Makefile # Automatically re-pip's when the metadata updates, runs tests, tests depend on up-to-date metadata, etc.
# Project Documentation
CONTRIBUTING.md # For open-source projects AND private / commercial ones. Have standards!
LICENSE.md
README.md # Containing installation, configuration, testing, and runtime use instructions.
# Python Packaging
setup.cfg # Contains the vast majority of the project metadata, all but some minor versioning configuration.
setup.py # Contains the aformentioned `use_scm_version` configuration.
# Directory for GitHub repository metadata.
.github/
ISSUE_TEMPLATE/ # Directory for, at a minimum:
bug-report.md
request.md
FUNDING.yml # GitHub Sponsors, Patreon, ...
# Don't forget the issue labels: https://github.com/marrow/marrow-package-template/labels
# Directory for application hosting service metadata, e.g. "clevercloud".
{hosting-provider}/
cron.json # Any periodic tasks the application requires are scheduled here.
# Alternative: apscheduler in-app cron, but careful with dynamic scaling!
# (How many workers does the job need to run on? Which ones? ...)
http.json # HTTPS-forced.
# The rest is controlled, on my hosting, via environment variables (.env).
# Convienent pre-constructed environments.
env/
{hosting-provider}.example # Production / staging configuration template.
default # In-development configuration.
...
# Static assets served by the front-end load balancer (e.g. Nginx, Apache, ...) in production.
# Relatively shallow, except the CSS source. Served by the application itself in development.
# Served by a front-end high-performance HTTP server, or CDN, in production environments.
public/
css/
font/
img/
js/
scss/
element/ # Contains various re-usable "component" definitions.
_fonts.sass
_mixins.sass
_typography.sass
_utility.sass
_variables.sass
base.scss
locale.scss
reset.scss
structural.scss
OpenSearch.xml # http://www.opensearch.org/
favicon.ico # https://en.wikipedia.org/wiki/Favicon
humans.txt # http://humanstxt.org/
# Dublin.rdf served by application -- http://dublincore.org/
# sitemap.xml served by application -- https://www.sitemaps.org/
# Test suite. Hopefully extensive! ;)
test/
...
# "Simple" applications that don't require heavy modularization so as to be isolatable and independently scalable
# utilize a simplified, single-level approach where the "web.app.name" is the namespace for everything. However,
# see 1b below, this structure can be packaged up and nested in a fractally-repeating pattern for modularity.
# Python 3 namespace packaging.
web/app/{app-name}/
api/ # Base module path for all "API interactions", e.g. "controllers" or "endpoints".
model/ # Base module path for all "models" (**not just data!**) and business logic.
util/ # Base module path for any non-specific additional routine or structure.
view/ # Base module path for all "templates".
__init__.py # If this application provides useful re-usable things, expose them here.
__main__.py # Produce a global WSGI application object (e.g. for UWSGI).
# Additionally, this is to support: python -m web.app.app-name to run a development server.
# Here, each "component" is developed using a module in as many of these "top level" directories as needed.
# The pattern should become quickly apparent in this "forum" example, and this makes the most sense as these
# "components" are not in any way independent of each-other.
web/app/forum/
api/
forum.py
response.py
thread.py
model/
forum.py
response.py
thread.py
util/
__init__.py
...
view/
__init__.py
forum.py
response.py
thread.py
root.py # The "web application root", the entry point into the "controller tree" / API.
# More complex applications tend to have a few isolatable components, so bunching absolutely everything together is
# disadvantageous, and becomes cluttered. Without a clear mandate towards solving focused concerns and isolation,
# it can eventually become nearly impossible to isolate and scale individual components of the overall system.
# This has killed projects. Luckily, the above "single-task project" structure, as it grows, can be easily pivoted
# to a more rigid (and scalable) structure. Again, the pattern should become obvious, skipping the unrelated metadata:
# (Notice how the above has become incorporated as just one component...)
web/app/shop/
product/
api.py # Reachable endpoints.
model.py # Data schemas and interaction logic.
util.py # Optional; any assorted utilities specific to this area.
view.py # A template; optionally this can *instead* be a sub-package.
view/ # If requiring more than one:
__init__.py # Ensure this is interpreted as a package, not a namespace.
... individual template files / serializers ...
invoice/
... as per product ...
customer/
... as per product ...
discount/
... as per product ...
feed/
... as per product ...
forum/
api/
forum.py
response.py
thread.py
model/
forum.py
response.py
thread.py
view/ # Example of having several logically distinct templates, rather than mashing into one `view.py`.
__init__.py
forum.py
response.py
thread.py
util/
__init__.py
...
root.py # The "web application root", the entry point into the "controller tree" / API.
# OR... for scaling...
root/
invoice.py # A web application root only incorporating the "invoice" section.
feed.py # A root only containing the "RSS feeds" section.
forum.py # A root only containing the "forum" section.
root.py # A root containing everything.
# The root containing everything is used in development.
# In production, it's also used, but the front-end load balancer directs requests to /invoice, /feed, or /forum at
# separate, independent scalers (clusters of "workers" servicing discrete applications) to spread the load.
# This, of course, only after profiling/benchmarking or otherwise identifying a bottlenck or issue.
# Don't let premature optimizaiton bite you, my main application at work went three years before needing to use
# separate scaler groups for certain sub-sections. (RSS feed generation is painful.)