1. Introduction
Since I started my journey with programming, I noticed an interesting pattern: the majority of applications are templated. Yes, it’s a pure fact! Just stop right here, at the beginning of this article, and start thinking of all the projects you have developed.
What do they have in common? If you look closely, you’ll see that many core functionalities are reused across different projects. These core functionalities often include user authentication, payment processing, user management, and more.
In this article, I would like to raise the point that all of these patterns were already created by programmers from the past.Really, almost everything we are using right now, was already implemented. We just modify some functionality based on the specific project.
I will introduce you to examples from the backend development perspective in Python, but this can be applied to any programming language or any field in the realm of software engineering.
So, what do all backend applications have in common? Let’s take a look!
Note: If you are familiar with OOP (Object Oriented Programming), consider your templated modules as the highest level of abstraction but on the application level so that this should be written according to this principle.
2. Authentication and Authorization
I would like to break down each further section to the basic components, which can be applied almost to any backend application.
Basic Components
- User Model: A representation of the user that includes attributes like username, password, email, roles, etc.
- Password Management: Functions to hash and verify passwords.
- Token Generation: Mechanism to generate and verify tokens (JWT, OAuth2, etc.).
- Middleware/Decorator: Protect routes/endpoints that require authentication.
- Role Management: Assign and verify user roles and permissions.
2.1 User Model
We are defining the most generic class of the User
with attributes that can be applied to any specific user.
from werkzeug.security import generate_password_hash, check_password_hash
class User:
def __init__(self, username, password, email):
self.username = username
self.password_hash = generate_password_hash(password)
self.email = email
self.roles = []
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
2.2 Token Management
Using JWT for token-based authentication is a good choice in terms of cybersecurity and best practices in backend development.
def generate_token(user):
payload = {
'username': user.username,
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
}
return jwt.encode(payload, SECRET_KEY, algorithm='HS256')
def verify_token(token):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
return payload['username']
except jwt.ExpiredSignatureError:
return None
except jwt.InvalidTokenError:
return None
2.3 Decorators
- Decorator to check if the user is allowed to access the page.
from functools import wraps
from flask import request, jsonify, session
def is_authenticated(func):
@wraps(func)
def decorated_function(*args, **kwargs):
if 'user' not in session:
return jsonify({"error": "User not authenticated"}), 401
return func(*args, **kwargs)
return decorated_function
- Decorator to check the role of the user.
def roles_required(*roles):
def decorator(func):
@wraps(func)
def decorated_function(*args, **kwargs):
user_roles = session.get('roles', [])
if not any(role in user_roles for role in roles):
return jsonify({"error": "User does not have the required role"}), 403
return func(*args, **kwargs)
return decorated_function
return decorator
And basically, that’s it! You can use this predefined functionality for authentication across all projects!
3. Payments and Billing
Almost any application handles financial transactions. Whether it’s a local butcher shop or a big enterprise giant, you will need to use an effective system to gather payments.
Basic Components
- Payment Gateway Integration: Connecting with payment gateways like
Stripe
orPayPal
- Payment Models: Defining models to handle payment data.
- Payment Processing: Handling the payment lifecycle (initiate, confirm, etc.).
3.1 Payment Gateway Integration
This can be used as a base for integrating different payment gateways, with a concrete implementation for Stripe. Generally, my personal preference is to use StripeAPI
for payments as it has been for a long time on the market, and really, easy to integrate into any project.
class PaymentGateway(ABC):
@abstractmethod
def create_payment_intent(self, amount, currency='gbp'):
pass
@abstractmethod
def confirm_payment(self, payment_id):
pass
@abstractmethod
def handle_webhook(self, payload, sig_header):
pass
This is the most generic example for the payment gateway, and you can focus on specific implementation according to your needs.
3.2 Payment Models
Define models to store payment information. This example can be adapted for use with ORM. You may create a more complex hierarchy of classes if needed, but for this example, the following snippet should be pretty sufficient.
class Payment:
def __init__(self, user_id, amount, currency):
self.id = uuid.uuid4()
self.user_id = user_id
self.amount = amount
self.currency = currency
self.status = status
payments = []
Save all payments into the database and set up a Celery
task for processing transactions, for the 3.3 section. The database records should look like the following:
id | user_id | amount | currency | status
--------------------------------------+-----------------------------------+--------+----------+----------
e532d653-7c8b-453a-8cd4-3ab956863d72 | 1ff9efb3-d5e8-4e53-854f-4246ba9ff638 | 100.00 | USD | Failed
35985d41-5d54-4021-bed6-82d7233cc353 | a0984002-bace-478e-b6f9-6e4459e1b5ba | 250.50 | EUR | Pending
1ff9efb3-d5e8-4e53-854f-4246ba9ff638 | 9f896874-dc43-4592-8289-d0f7f8b8583a | 99.99 | GBP | Completed
Now, we have created a complex system that can be integrated into any project. Are you still following the pattern? This can be used EVERYWHERE!
After all, you can define another application for visualization of this data. You’ve got the point about templating! 😉
4. Email and Notification Services
Email and notifications keep users informed and engaged in the life of your app. Whether it's for account verification, password resets, or marketing communications, a reliable email service is essential for any type of project.
- Email Service Integration: Connecting with email services like
SendGrid
orAmazon SES
. - Email Templates: Defining templates for various email types.
- Sending Emails: Functions to send emails using the integrated service.
4.1 Email Service Integration
Define the main logic of SendGrid
for sending emails inside EmailService
class.
import sendgrid
from sendgrid.helpers.mail import Mail
class EmailService:
def __init__(self, api_key):
self.sg = sendgrid.SendGridAPIClient(api_key)
def send_email(self, from_email, to_email, subject, html_content):
email = Mail(
from_email=from_email,
to_emails=to_email,
subject=subject,
html_content=html_content
)
try:
response = self.sg.send(email)
return response.status_code
except Exception as e:
return str(e)
As with payment gateway, you don’t need to focus on any specific utils or products on the market. This is just an example of how it can be generalized and templated for any project.
4.2 Email Templates
My favorite pattern for systems like this is the handlers pattern; you just add more and more keys to the dictionary as a type of email, and the path to the file with a content.
email_templates = {
'welcome': “welcome.html”,
'reset_password': "<h1>Reset Your Password</h1><p>Click <a href='{link}'>here</a> to reset your password.</p>"
}
Otherwise, it could be nice to define an Enum
for the same purposes.
4.3 Sending Emails
We need a function to make the magic happen! Let’s write the following:
def send_email(email_service, from_email, to_email, subject, template_name, **template_vars):
"""
Send an email using the specified email service.
"""
html_content = get_email_template(template_name, **template_vars)
return email_service.send_email(from_email, to_email, subject, html_content)
Another important point would be adding several files to all backend projects, such as README
.env
config.py
, pyproject.toml,
.pre-commit.yml
and come up with the base structure of the files inside the project.
Though the suggested structure is a little bit tightened to Python implementation, as I mentioned before, you can do the same for any other language or field.
It is also important to note that templating at the highest level of abstraction and maintaining a good structure of the application can be
reused for other projects as a package or an addition to the microservice architecture.
5. Conclusion
EVERYTHING CAN BE TEMPLATED!
The examples provided here are just the beginning—these patterns can be extended and refined to cover more complex scenarios as your projects evolve. You may add caching
establish k8s
, docker
, Devops
infrastructure, CI/CD
and pipelines.
Remember one simple statement: Once you have done your job properly, you can use the same job while completing another.
The goal is to make the project, infrastructure, components, and services reusable across different applications!
Make yourself a cup of tea, and think about which parts of your applications can be re-used in different apps. Try to create similar services and automate your job, adjusting only some code pieces!
Thanks for reading, and happy templating!