Васильев Андрей Михайлович, 2023
Версии презентации
Процесс проектирования архитектуры приложения может быть
Архитектура приложения — это комбинация выбранных подходов к формированию приложения, а также результирующая структура исходного кода
Обычно архитектура проявляется в подходе к разделению зон ответственности исходного кода приложения на части
Приложение разделяется по логически связным частям предметной области, для каждой функции выделяются отдельные части приложения
Для каждой из этих областей можно выделить класс или набор классов в выделенном пакете
Любое достаточно сложное приложение содержит нетривиальную логику по модификации данных, эту логику удобно отделить от технической части приложения
Относительно веб-приложения можно рассмотреть следующие части
Можно предложить следующий подход к разделению небольшого приложения на части:
.domain
.domain.storage
или .domain.database
.domain.operations
.public
.web
.web.filters
.web.handlers
.web.models
.web.validation
Вход — запрос от клиента, клиентом может выступать:
Выход — ответ сервера
Клиентское приложение должно всегда получать корректный ответ. Если был составлен некорректный запрос, то приложение должно оповещать об этом
Для каждого случая необходимо подготовить разумную стратегию обработки ошибок
fun interface Filter : (HttpHandler) -> HttpHandler
Фильтры позволяют выполнить действия, независимые от конкретного запроса:
Фильтр принимает в качестве аргумента HttpHandler и должен вернуть HttpHandler
val handler = { _: Request -> Response(OK) }
val myFilter = Filter {
next: HttpHandler -> {
request: Request ->
val start = System.currentTimeMillis()
val response = next(request)
val latency = System.currentTimeMillis() - start
println("I took $latency ms")
response
}
}
val latencyAndBasicAuth: Filter = ServerFilters.BasicAuth(
"my realm", "user", "password")
.then(myFilter)
val app: HttpHandler = latencyAndBasicAuth.then(handler)
Пакет org.http4k.filter содержит описание фильтров, поставляемых с помощью http4k
Подход к реализации:
Наивный подход к реализации
val errorFilter = Filter { next: HttpHandler ->
{ request: Request ->
val response = next(request)
if (response.successfull) {
response
} else {
response.body(renderer(ErrorMessage(request)))
}
}
}
val application = errorFilter.then(router)
Для всех данных, которые передаёт пользователь необходимо выполнить две базовые проверки:
Подходы к обработке данной ситуации:
Рассмотрим порядок обработки маршрута с переменной внутри. Стратегия решения проблемы — отображение сообщения, что искомый элемент не был найден
fun handler(
renderer: TemplateRenderer,
getEntityByData: (Int) -> Entity?,
): HttpHandler = handler@{ request ->
val paramemter: String? = request.path("parameter")?.toIntOrNull()
if (parameter == null) {
return@handler Response(UNSATISFIABLE_PARAMETERS).body(
renderer(EmptyParameterRequestFoundVM())
)
}
val entity: Entity? = getEntityByData(parameter)
if (entity == null) {
return@handler Response(NOT_FOUND).body(
renderer(EntityNotFoundVM())
)
}
Response(OK).body(renderer(ShowEntityVM(entity)))
}
Или с помощью составления цепочки по обработке данных, которые могут
стать null
:
fun handler(
renderer: Templaterenderer,
getEntityByData: (Int) -> Entity?,
): HttpHandler = { request ->
request
.path("parameter")
?.let { parameter ->
getEntityByParameter(parameter)
}?.let { entity ->
Response(OK).body(renderer(ShowEntityVM(entity)))
} ?: Response(NOT_FOUND).body(renderer(EntityNotFoundVM()))
}
Или путём обработки данной ситуации на уровне шаблона Pebble
Разрешаем null-состояние для модели
data class ShowEntityVM(entity: Entity?)
Передаём неопределённое состояние в модель
fun handler(
renderer: Templaterenderer,
getEntityByData: (Int) -> Entity?,
): HttpHandler = { request ->
val entity request
.path("parameter")
?.let { parameter ->
parameter.toIntOrNull()
}?.let { number ->
getEntityByParameter(parameter)
}
Response(OK).body(renderer(ShowEntityVM(entity)))
}
Проверка на null в шаблоне Pebble:
{% if model.entity is null %}
...
{% else %}
...
{% endif %}
Однако такой подход не следует активно использовать:
В рамках курса такой подход запрещён