Шаблоны в разработке программного обеспечения: более глубокий взгляд
С тех пор как я начал свой путь в программировании, я заметил интересную закономерность: большинство приложений шаблонны. Да это чистый факт! Просто остановитесь прямо здесь, в начале этой статьи, и начните думать обо всех проектах, которые вы разработали.
Что они имеют общего? Если вы присмотритесь, вы увидите, что многие основные функции повторно используются в разных проектах. Эти основные функции часто включают аутентификацию пользователей, обработку платежей, управление пользователями и многое другое.
В этой статье я хотел бы подчеркнуть, что все эти шаблоны уже были созданы программистами прошлого. Действительно, почти все, что мы используем сейчас, уже реализовано. Мы просто модифицируем некоторый функционал исходя из конкретного проекта.
Я познакомлю вас с примерами серверной разработки на Python, но это можно применить к любому языку программирования или любой области разработки программного обеспечения.
Итак, что общего у всех серверных приложений? Давайте взглянем!
Примечание. Если вы знакомы с ООП (объектно-ориентированным программированием), рассматривайте свои шаблонные модули как высший уровень абстракции, но на уровне приложения, поэтому их следует писать в соответствии с этим принципом.
Аутентификация и авторизация
Я хотел бы разбить каждый следующий раздел на базовые компоненты, которые можно применить практически к любому серверному приложению.
Основные компоненты
- Модель пользователя: представление пользователя, включающее такие атрибуты, как имя пользователя, пароль, адрес электронной почты, роли и т. д.
- Управление паролями: функции для хэширования и проверки паролей.
- Генерация токенов: механизм генерации и проверки токенов (JWT, OAuth2 и т. д.).
- Промежуточное программное обеспечение/декоратор: защищает маршруты/конечные точки, требующие аутентификации.
- Управление ролями: назначение и проверка ролей и разрешений пользователей.
Модель пользователя
Мы определяем наиболее общий класс 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)
Управление токенами
Использование JWT для аутентификации на основе токенов — хороший выбор с точки зрения кибербезопасности и лучших практик серверной разработки.
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
Декораторы
Декоратор, проверяющий, разрешен ли пользователю доступ к странице.
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
Декоратор для проверки роли пользователя.
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
И в принципе, вот и все! Вы можете использовать эту предопределенную функцию для аутентификации во всех проектах!
Платежи и выставление счетов
Практически любое приложение обрабатывает финансовые транзакции. Будь то местная мясная лавка или крупный гигант, вам потребуется использовать эффективную систему сбора платежей.
Основные компоненты
- Интеграция платежных шлюзов: подключение к платежным шлюзам, таким как Stripe или PayPal.
- Модели оплаты: определение моделей для обработки платежных данных.
- Обработка платежей: обработка жизненного цикла платежа (инициирование, подтверждение и т. д.).
Интеграция платежного шлюза
Это можно использовать в качестве основы для интеграции различных платежных шлюзов с конкретной реализацией Stripe
. В целом, я лично предпочитаю использовать StripeAPI
для платежей, поскольку он уже давно присутствует на рынке и действительно легко интегрируется в любой проект.
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
Это наиболее общий пример платежного шлюза, и вы можете сосредоточиться на конкретной реализации в соответствии с вашими потребностями.
Модели оплаты
Определите модели для хранения платежной информации. Этот пример можно адаптировать для использования с ORM. При необходимости вы можете создать более сложную иерархию классов, но для этого примера следующего фрагмента будет вполне достаточно.
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 = []
Cохраните все платежи в базу данных и настройте задачу Celery
для обработки транзакций. Записи базы данных должны выглядеть следующим образом:
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
Теперь мы создали сложную систему, которую можно интегрировать в любой проект. Вы все еще следуете шаблону? Это можно использовать ВЕЗДЕ!
Ведь вы можете определить другое приложение для визуализации этих данных. Вы поняли суть шаблонов! 😉
Службы электронной почты и уведомлений
Электронная почта и уведомления позволяют пользователям быть в курсе событий и участвовать в жизни вашего приложения. Будь то проверка учетной записи, сброс пароля или маркетинговые коммуникации, надежная служба электронной почты необходима для любого типа проекта.
- Интеграция службы электронной почты: подключение к службам электронной почты, таким как
SendGrid
илиAmazon SES
.
- Шаблоны электронной почты: определение шаблонов для различных типов электронной почты.
- Отправка электронных писем: функции для отправки электронных писем с помощью интегрированной службы.
Интеграция службы электронной почты
Определите основную логику SendGrid
для отправки электронных писем внутри класса EmailService
.
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)
Как и в случае с платежным шлюзом, вам не нужно сосредотачиваться на каких-либо конкретных утилитах или продуктах на рынке. Это всего лишь пример того, как это можно обобщить и шаблонизировать для любого проекта.
Шаблоны электронных писем
Мой любимый шаблон для подобных систем — шаблон обработчиков; вы просто добавляете в словарь все больше и больше ключей как типа письма, так и пути к файлу с содержимым.
email_templates = {
'welcome': “welcome.html”,
'reset_password': "<h1>Reset Your Password</h1><p>Click <a href='{link}'>here</a> to reset your password.</p>"
}
В противном случае было бы неплохо определить Enum
для тех же целей.
Отправка электронных писем
Нам нужна функция, чтобы творить волшебство! Напишем следующее:
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)
Еще одним важным моментом будет добавление нескольких файлов во все серверные проекты, таких как README .env config.py
, pyproject.toml
, .pre-commit.yml
, и создание базовой структуры файлов внутри проекта.
Хотя предложенная структура немного ограничена реализацией Python, как я уже упоминал ранее, вы можете сделать то же самое для любого другого языка или области.
Также важно отметить, что создание шаблонов на самом высоком уровне абстракции и поддержание хорошей структуры приложения могут быть эффективными.
Повторно использоваться в других проектах в качестве пакета или дополнения к микросервисной архитектуре.
Вывод
ВСЕ МОЖНО СДЕЛАТЬ ПО ШАБЛОНУ!
Приведенные здесь примеры — это только начало: эти шаблоны можно расширять и уточнять, чтобы охватить более сложные сценарии по мере развития ваших проектов. Вы можете добавить кэширование, установить k8, докер, инфраструктуру Devops, CI/CD и конвейеры.
Запомните одно простое утверждение: как только вы выполнили свою работу должным образом, вы можете использовать ту же работу при выполнении другой.
Цель состоит в том, чтобы сделать проект, инфраструктуру, компоненты и сервисы повторно используемыми в различных приложениях!
Заварите себе чашку чая и подумайте, какие части ваших приложений можно повторно использовать в других приложениях. Попробуйте создать подобные сервисы и автоматизировать свою работу, корректируя лишь некоторые участки кода!
Спасибо за прочтение и удачного создания шаблонов!