Русский
Русский
English
Статистика
Реклама

Бизнес-логика

Vue.js и слоистая архитектура вынесение бизнес-логики в сервисы

10.05.2021 12:10:00 | Автор: admin

Когда нужно сделать код в проекте гибким и удобным, на помощь приходит разделение архитектуры на несколько слоев. Рассмотрим подробнее этот подход и альтернативы, а также поделимся рекомендациями, которые могут быть полезны как начинающим, так и опытным разработчикам Vue.js, React.js, Angular.

В старые времена, когда JQuery только появился, а о фреймворках для серверных языков лишь читали в редких новостях, веб-приложения реализовывали целиком на серверных языках. Зачастую для этого использовали модель MVC (Model-View-Controller): контроллер (controller) принимал запросы, отвечал за бизнес-логику и модели (model) и передавал данные в представление (view), которое рисовало HTML.

Объектно-ориентированное программирование (ООП) на тот момент только начинало формироваться, поэтому разработчики зачастую интуитивно решали, где и какой код надо писать. Таким образом, в мире разработки зародилось такое понятие, как Божественные объекты, которые первоначально отвечали практически за всю работу отдельных частей системы. Например, если в системе была сущность Пользователь, то создавался класс User и в нем писалась вся логика, так или иначе связанная с пользователями. Без разбиения на какие-то ещё файлы. И если приложение было большим, то такой класс мог содержать тысячи строк кода.

Затем появились первые фреймворки, работать с ними стало удобнее, но они не учили, как правильно заложить структуру, архитектуру проекта. И разработчики продолжали писать тысячи строк кода в контроллерах новомодных фреймворков.

1. Выход есть

Как известно, Vue.js, React.js и прочие подобные фреймворки основаны на компонентах. То есть, по большому счету, приложение состоит из множества компонентов, которые могут заключать в себе и бизнес-логику и представление и много чего еще. Таким образом, разработчики во многих проектах пишут всю логику в компонентах и эти компоненты, как правило, начинают напоминать те самые божественные классы из прошлого. То есть, если компонент описывает какую-то крупную часть функционала с большим количеством (возможно сложной) логики, то вся эта логика и остается в компоненте. Появляются десятки методов и тысячи строк кода. А если учесть то, что, например, во Vue.js еще есть такие понятия как computed, watch, mounted, created, то логику пишут еще и во все эти части компонента. В итоге, чтобы найти какую-то часть кода, отвечающую за клик по кнопке, надо перелистать десяток экранов js-кода, бегая между methods, computed и прочими частями компонента.

Примерно в 2008 году, применительно к backend, была предложена слоистая архитектура. Основная идея этой архитектуры заключается в том, что весь код приложения следует разбивать на определенные слои, которые выполняют определенную работу и не очень знают о других слоях.

С подобным разбиением приложение становится намного проще поддерживать, писать тесты, искать ответственные зоны и вообще читать код.

Вот о таком разбиении кода на слои и пойдет речь, но уже применительно к frontend-фреймворкам, таким как Vue.js, React.js и прочим.

Изначальная теория слоистой архитектуры, применительно к backend, имеет много ограничений и правил. Идея же этой статьи в том, чтобы перенять именно разбиение кодовой базы на слои. Схематично ее можно изобразить примерно так.

2. Создание удобной архитектуры приложения

Рассмотрим пример, в котором вся логика находится в одном компоненте.

2.1. Логика в компоненте

Рассматриваемый компонент отвечает за работу с коллажами, в частности за дублирование, восстановление и удаление. В нем уже используются некоторые сервисы, но все равно в компоненте много бизнес-логики.

methods: {    duplicateCollage (collage) {      this.$store.dispatch('updateCollage', { id: collage.id, isDuplicating: true })      dataService.duplicateCollage(collage, false)        .then(duplicate => {          this.$store.dispatch('updateCollage', { id: collage.id, isDuplicating: false })        })        .catch(() => {          this.$store.dispatch('updateCollage', { id: collage.id, isDuplicating: false })          this.$store.dispatch('errorsSet', { api: `We couldn't duplicate collage. Please, try again later.` })        })    },    deleteCollage (collage, index) {      this.$store.dispatch('updateCollage', { id: collage.id, isDeleting: true })      photosApi.deleteUserCollage(collage)        .then(() => {          this.$store.dispatch('updateCollage', {            id: collage.id,            isDeleting: false,            isDeleted: true          })          this.$store.dispatch('setUserCollages', { total: this.userCollages.total - 1 })          this.$store.dispatch('updateCollage', {            id: collage.id,            deletingTimer: setTimeout(() => {              this.$store.dispatch('updateCollage', { id: collage.id, deletingTimer: null })              this.$store.dispatch('setUserCollages', { items: this.userCollages.items.filter(userCollage => userCollage.id !== collage.id) })               // If there is no one collages left - show templates              if (!this.$store.state.editor.userCollages.total) {                this.currentTabName = this.TAB_TEMPLATES              }            }, 3000)          })        })    },    restoreCollage (collage) {      clearTimeout(collage.deletingTimer)      photosApi.saveUserCollage({ collage: { deleted: false } }, collage.id)        .then(() => {          this.$store.dispatch('updateCollage', {            id: collage.id,            deletingTimer: null,            isDeleted: false          })          this.$store.dispatch('setUserCollages', { total: this.userCollages.total + 1 })        })    }}

2.2. Создание слоя сервисов для бизнес-логики

Для начала можно ввести в приложение сервисный слой, который будет отвечать за бизнес-логику.

Один из классических способов хоть какого-то разбиения логики это деление на сущности. Например, почти всегда в проекте есть сущность Пользователь или, как в описываемом примере, Коллаж. Таким образом, можно создать папку services и в ней файлы user.js и collage.js. Такие файлы могут быть статическими классами или просто возвращать функции. Главное чтобы вся бизнес-логика, связанная с сущностью, была в этом файле.

services  |_collage.js  |_user.js

В сервис collage.js следует поместить логику дублирования, восстановления и удаления коллажей.

export default class Collage {  static delete (collage) {    // ЛОГИКА УДАЛЕНИЯ КОЛЛАЖА  }   static restore (collage) {    // ЛОГИКА ВОССТАНОВЛЕНИЯ  КОЛЛАЖА  }   static duplicate (collage, changeUrl = true) {    // ЛОГИКА ДУБЛИРОВАНИЯ КОЛЛАЖА  }}

2.3. Использование сервисов в компоненте

Тогда в компоненте надо будет лишь вызвать соответствующие функции сервиса.

methods: {  duplicateCollage (collage) {    CollageService.duplicate(collage, false)  },  deleteCollage (collage) {    CollageService.delete(collage)  },  restoreCollage (collage) {    CollageService.restore(collage)  }}

С таким подходом методы в компоненте будут состоять из одной или нескольких строчек кода, а логика, связанная с коллажами, будет инкапсулирована в соответствующем файле collage.js, а не размазана по огромному компоненту, соответственно, будет проще искать нужный код, поддерживать его и писать тесты. Еще один плюс такого подхода в том, что код из сервисов можно переиспользовать в любом месте проекта.

Также многие разработчики на пути к удобной архитектуре выносят вызовы методов API в отдельный файл (файлы). Это как раз создание слоя вызовов API, которое также приводит к удобству и структурированности кода.

import axios from '@/plugins/axios' export default class Api {   static login (email, password) {    return axios.post('auth/login', { email, password })      .then(response => response.data)  }   static logout () {    return axios.post('auth/logout')  }   static getCollages () {    return axios.get('/collages')      .then(response => response.data)  }    static deleteCollage (collage) {    return axios.delete(`/collage/${collage.id}`)      .then(response => response.data)  }    static createCollage (collage) {    return axios.post(`/collage/${collage.id}`)      .then(response => response.data)  }}

3. Что и куда выносить?

На вопрос, что же именно и куда выносить, однозначно ответить невозможно. Как вариант, можно разбить код на три условные части: бизнес-логика, логика и представление.

Бизнес-логика это все то, что описано в требованиях к приложению. Например, ТЗ, документации, дизайны. То есть все то, что напрямую относится к предметной области приложения. Примером может быть метод UserService.login() или ListService.sort(). Для бизнес-логики можно создать сервисный слой с сервисами.

Логика это тот код, который не имеет прямого отношения к предметной области приложения и его бизнес-логике. Например, создание уникальной строки или поиск некоего объекта в массиве. Для логики можно создать слой хэлперов: например, папку helpers и в ней файлы string.js, converter.js и прочие.

Представление все то, что непосредственно связано с компонентом и его шаблоном. Например, изменение реактивных свойств, изменение состояний и прочее. Этот код пишется непосредственно в компонентах (methods, computed, watch и так далее).

login (email, password) {  this.isLoading = true  userService.login(email, password)    .then(user => {      this.user = user      this.isLoading = false    })}

Далее в компонентах надо будет вызывать сервисы, а сервисы будут использовать хэлперы. Такой подход предоставит нам легкие, маленькие и простые компоненты, а вся логика будет находиться в логически понятных файлах.

Если же сервисы или хэлперы начнут разрастаться, то сущности всегда можно разделить на другие сущности. К примеру, если у пользователя в приложении маленький функционал в 3-5 методов и пара методов про заказы пользователя, то разработчик может вынести всю эту бизнес-логику в сервис user.jsрешить всю эту бизнес-логику написать в сервисе user.js. Если же у сервиса пользователя сотни строк кода, то можно все, что относится к заказам, вынести в сервис order.js.

4. От простого к сложному

В идеале можно сделать архитектуру на ООП, в которой будут, помимо сервисов, еще и модели. Это классы, описывающие сущности приложения. Те же User или Collage. Но использоваться они будут вместо обычных объектов данных.

Рассмотрим список пользователей.

Классический способ вывода ФИО пользователей выглядит так.

<template><div class="users">  <div    v-for="user in users"    class="user"  >    {{ getUserFio(user) }}  </div></div></template> <script>import axios from '@/plugins/axios' export default {  data () {    return {      users: []    }  },  mounted () {    this.getList()  },  methods: {    getList() {      axios.get('/users')        .then(response => this.users = response.data)    },    getUserFio (user) {      return `${user.last_name} ${user.first_name} ${user.third_name}`    }  }}</script>

Функцию получения ФИО можно вынести для того, чтобы легко и просто переиспользовать при необходимости.

Для начала следует создать модель Пользователь.

export default class User {  constructor (data = {}) {    this.firstName = data.first_name    this.secondName = data.second_name    this.thirdName = data.third_name  }   getFio () {    return `${this.firstName} ${this.secondName} ${this.thirdName}`  }}

Далее следует импортировать эту модель в компонент.

import UserModel from '@/models/user'

С помощью сервиса получить список пользователей и преобразовать каждый объект в массиве в объект класса (модели) User.

methods: {   getList() {     const users = userService.getList()     users.forEach(user => {       this.users.push(new UserModel(user))     })   },

Таким, образом, в шаблоне или в методах не надо будет создавать какие-то отдельные функции для работы с объектом пользователя, они будут уже внутри этого объекта.

<template><div class="users">  <div    v-for="user in users"    class="user"  >    {{ user.getFio() }}  </div></div></template>

К вопросу о том, какую логику выносить в модели, а какую в сервисы. Можно всю логику поместить в сервисы, а в моделях вызывать сервисы. А можно в моделях хранить логику, относящуюся непосредственно к сущности модели (тот же getFio()), а логику работы с массивами сущностей хранить в сервисах (тот же getList()). Как будет удобнее.

5. Заключение

Если в проекте большое количество логики хранится в компонентах, есть риск сделать их трудночитаемыми и осложнить дальнейшее переиспользование логики. В таких случаях можно ввести слои для вынесения этой логики: например, слой сервисов для бизнес-логики, слой хэлперов для остальной логики. Внутри компонента стоит оставить ту логику, которая относится непосредственно к нему и его шаблону.

Также для удобства можно создать слои для операций с сессиями, перехватчиками (interceptors) api, глобальными обработчиками ошибок смотря что вам будет удобно. Таким образом, вы сделаете компоненты маленькими и простыми, а логика будет хранитьсятам, где ее легко будет найти и переиспользовать в любом месте проекта.

Спасибо за внимание! Будем рады ответить на ваши вопросы.

Подробнее..

Я десять лет страдал от ужасных архитектур в C приложениях и вот нашел, как их исправить

01.09.2020 18:20:28 | Автор: admin


Я второй десяток лет участвую в разработке приложений для бизнеса на .NET и каждый раз вижу одни и те же проблемы быдлокод и беспорядок. Месиво из сервисов, UoW, DTO-шек, классов-хелперов. В иных местах и прямой доступ в базу данных руками, логика в статических классах, километровые портянки конфигурации IoC.


Когда я был молодым и резвым мидлом я тоже так писал. Потом бил кулаком в стену с криками: "Хватит! В следующий раз сделаю по-другому". Следующий раз действительно начинался "по-другому" с холодной головой и строгим подходом к архитектуре а на выходе все равно получалась та же субстанция, лучше на пару миллиметров.


Однако, эволюция беспощадная штука: моя последняя система показалась мне более-менее близкой к идеалу. Сложность не сильно росла, скорость разработки не падала довольно долго, в систему худо-бедно въезжают новые сотрудники. Эти результаты я взял за основу, улучшил и теперь анонсирую вам свою новую разработку: Reinforced.Tecture.


Откуда берётся бардак


Вопрос, откуда в наших приложениях текут реки зловонного кода, я задавал огромному количеству людей. Говорят, мест много и вообще пенять надо на бизнес, постоянно меняющиеся требования, нанятых за копейку джунов, ошибки управления проектом, сжатые сроки Это всё правда, но бизнес мы изменить никогда не сможем, а вот приспособить код наших приложений к подобным условиям и страдать чуть меньше вполне в наших силах.


Если отбросить человеческие аспекты и оставить технически-архитектурные, то откуда берется весь бардак?


Это IoC!


Нет, вы только поймите правильно: инверсия зависимостей и лайфтайм-менеджмент из одной точки приложения это прекрасно. Но что лежит в наших контейнерах? Connection к базе данных (один на подключение), пачка каких-нибудь параметров и credentials, надёрганных из web.config (обычно синглтон) и огромные, бескрайние километры разнообразных сервисов-UoW-репозиториев. В худшем случае в формате интерфейс-реализации.


Мы это делаем, чтобы наши приложения в любой момент можно было разобрать на составные кусочки например, для тестирования или отдачи подсистемы на разработку другой команде. Цель благородная! Ради неё, наверное, можно хранить портянки IoC-конфигурации и по два файла для каждого компонента (интерфейс-реализация).


Или нет? И тут я хочу помолиться за команды, которые догадываются использовать механизм модулей, предоставляемый почти любым IoC-фреймворком: парни, вы делаете ну хоть что-то, крепитесь.


Это потому что нет unit-тестов!


Я вам расскажу почему их нет.


Вот давайте по чесноку: все же пробовали писать unit-тесты для традиционных C#-проектов, сделанных по методологии "UoW и Repository"? На всё вышеупомянутое содержимое контейнера надо написать заглушки и вбить тестовые данные (руками). Ну или пойти попросить себе виртуалку под тесты, на которую надо вкатить БД и залить данные, заботливо украденные с продакшена. И подчищать их после каждого прогона тестов.


В итоге на один честный тест одного метода бизнес-логики у вас уходит хорошо если день времени. А потом он начинает периодически падать потому что инфраструктура поменялась (базу переместили в другое место), коннекшн отвалился по таймауту, данные пришли позже и т.д. Стандартным ответом на это является всеми нами любимое "Билд упал? Да ерунда, перезапусти".


Такими темпами вы постепенно забиваете на написание тестов, ограничиваясь проверками каких-нибудь тривиальных хреновин вроде того, что метод копирования полей в класс из 10 строчек действительно, блин, копирует поля! Такие тесты остаются в системе навечно и всегда выполняются "для галочки", падая, может, раз в год, когда неопытный джун сдуру сотрёт строчку при мердже. Всё остальное время они представляют собой театр безопасности кода. Все прекрасно понимают что реальное тестирование происходит "вручную", QA-отделом (в лучшем случае автоматизировано, end-to-end), а пользователи вроде не жалуются.


Опытные разработчики, которым надо релизить по фиче в неделю и закрывать по 5 тасков в день этой чушью категорически маяться не хотят и в этом бизнес их поддерживает. Овчинка выделки не стоит. Дай боже, если на тесты набирают зелёненьких джуниоров QA Automation их время почти ничего не стоит нехай ковыряются.


Это потому что мы лезем в базу руками!


Да неужели? А я-то думал что вы, как настоящие мужики, терпите, пока O/RM удаляет 3000 объектов. Все, кто хочет чтобы их приложение более-менее сносно работало по скорости рано или поздно лезут в базу руками. Ну потому что она база. Потому что объектная модель натягивается на реляционную как сова на глобус долго, медленно и со слезами (см. object-relational impedance mismatch). А тут ещё и O/RM поощряет такие кренделя, оставляя доступ напрямую к базе (ибо как если этого не делать его пользователи с потрохами сожрут). Как же тут не соблазниться возможностью решить задачу быстро и просто, когда нужно медленно и сложно. Безусловно, любители посылать базе SQL из приложения сами виноваты. Однако делают это не от хорошей жизни.


Это не только базы касается имейлы тоже часто отправляют прямо из логики. И запросы ко внешним вёб-сервисам, и сообщения в очередь пихают. Очень смешно, кстати, получается, если транзакция в БД по каким-то причинам упала, а e-mail ушёл. Транзакцию-то можно откатить, а вот письмо уже обратно не всосёшь.


Это потому что мы не следуем паттернам!


Да кто ж вам мешает пожалуйста, следуйте. Но давайте, попробуем реализовать простую фичу. Что там надо? Написать репозиторий пользователей. Интерфейс + реализация, сделать то же самое для заказов, на основе них создать Unit of Work. Разумеется, тоже побив на интерфейс и реализацию. Потом сделать две DTOшки (а возможно больше), потом сервис, в котором использовать описанный Unit of Work, тоже разбив на две части.


Пол-дня прошло, у вас уже 10 файлов, описывающие 10 новых типов, а к разработке фичи вы ещё даже и не приступали. Потом созвон с заказчиком, обсуждение ТЗ, собеседование джуна и багфиксы. Фича же занимает своё место в долгом ящике.


Круто, если вам посчастливилось работать в огромной компании, где на IT выделяют чемодан золотых слитков в месяц, у вас 50 подопечных джунов-мидлов, а дедлайн проекта в следующем тысячелетии. Но вообще-то есть до чёрта маленьких команд, которые себе такой роскоши позволить не могут и делают как придётся.


И их продукты иногда стреляют, после чего переписывать прототип уже нет времени надо фичи деливерить! И если повезёт, то продукт разрастается, получает прибыль и инвестиции. А после нескольких таких фазовых переходов превращается в ту самую огромную IT-компанию, где золотые слитки и джуниоры.


Это потому что у нас нет архитектора!


Ну а что вы хотели? Тут и крепкого мидла днём с огнём не сыщешь, а в вашей компании едва ли умеют собеседовать архитекторов. А если и умеют, то по деньгам не потянут. Вот и пускают на эту должность всяких шарлатанов, знания которых ограничиваются в лучшем случае пониманием SOLID.


Да и не готовят особо нигде этих ваших архитекторов. Сами учатся ковырять UML, бедняги. Только вот нарисовать сколь угодно красивый дизайн, декомпозировать всё на классы и подсистемы, задействовать десяток бриджей, проксей и стратегий можно. И ревьюить весь код, самолично отрывая руки тем, кто посмеет нарушить целостность системы можно. И жарко спорить на созвонах о том, что выделить в интерфейс и куда положить метод тоже можно. Но это всё до первого дедлайна.


Выясняется, что красивая и грамотная архитектура (в 99% случаев) требует кучи телодвижений для решения простейших задач и внесения простейших правок. В итоге на красивый дизайн кладётся моржовый хрен и пишется так, чтобы быстрее решить задачу, учесть правки, пофиксить баг. Вершина пост-иронии если на самого архитектора падает 50 тикетов. Ах это незабываемое зрелище, ах эти незабываемые звуки! Принципиальный блюститель паттернов и правил сам начинает писать портянки попахивающего кода в обход всякой архитектуры. Пыхтит, корчится, шаблон трещит на весь офис лишь бы начальство по жопе не дало за сорванные сроки. Словами не передать: блажен, кто видел мир в его минуты роковые.


Жизнь сурова между "правильно, но долго" и "быстро, но неправильно" в критических условиях всегда выбирается второй вариант. Архитекторов, которые могут сделать так, чтобы правильно было проще, чем неправильно, я не встречал. Коль скоро оно так происходящее печально, но не удивительно.


Это потому что фрейморк XXX фигня!


Да, блин, большинство технологий фигня. Опенсорс работает плохо, платные решения тоже не без проблем. Только вот если вы откажетесь от XXX и всё перепишете это не спасёт. Но по другой, более фундаментальной причине.


Обычно тезис из подзаголовка идёт в комплекте с "давайте всё перепишем на YYY"! Именно такие сентенции выдают розовощёкие детишки, протерев лицо от печенек с конференционного кофе-брейка. Зуб даю, именно там они про волшебный YYY и услышали.


И если бы эти ребята поглощали печенья чуть меньше, а своей головой думали чуть больше они бы заметили, что YYY, который сделала для себя компания GGG ориентирован на решение проблем GGG. В выпуске подкаста "Мы обречены" со мной, Фил хорошо сформулировал на примере redux: "redux это фреймворк для управления состоянием Но не твоим состоянием!". И пусть вдохновляющие примеры а-ля "делаем на YYY приложение для подсчёта коров за 5 минут" не вводят вас в заблуждение. Между подсчётом коров и вашей условной системой автоматизации похоронного бизнеса всё же есть некоторая разница.


То есть вы увидели исключительно тот факт, что YYY хорошо ведёт себя на простых задачах, характерных для компании GGG. Надо обладать имплантированным электронным мозгом, подключённым параллельно с вашим, чтобы точно предсказать как YYY поведёт себя при решении сложных задач, характерных для вашей системы. Будет ли он так же хорош?


Очень часто технология YYY предсказуемо рвётся от несовместимости себя любимой с объективными реалиями вашей системы. А ещё бывает так, что YYY ещё незрелый, страдает от детских болезней и будет готов к production-использованию спустя пару лет. Если его использовать прямо сейчас, то проект или благополучно подохнет, или всё вернётся на круги своя, а розовощёкие мальчики поедут кушать ещё больше печенья и искать очередную серебряную пулю.


Как прибрать весь этот мусор


Впрочем, довольно хаять объективную реальность. Ныть всякий может. Лично я хочу иметь инструмент, который я могу взять при старте проекта, просто использовать его, и оно просто будет работать. Давайте формализуем моё "просто работать" в конкретные пункты, что хочется видеть в результате:


Жизнеспособность в долгосрочной перспективе


Я уже несколько раз в одно рыло апдейтил 2000 файлов в проекте (один раз даже с помощью VB.NET-C# транслятора), больше не хочу. Хочу видеть архитектурно такую систему, которая без разрывов в паху будет резаться на части и горизонтально расширяться логически.


Минимум кода, максимум логики


Ну не желаю я создавать по 100500 классов, чтобы соблюсти принципы дизайна. Вот просто нет. Хочу фигачить фичи с космической скоростью. И при этом не ронять maintainability, проламывая им пол и пришибая соседей снизу.


Я хочу так, чтобы сделать правильно было просто. То есть требовало меньше усилий, чем сделать неправильно. Вроде довольно чёткое и благородное стремление (хотя кому-то покажется взаимоисключающим).


Чёткая организация


Задолбало думать куда положить очередной сервис, в какую сборку поместить интерфейс, в какой неймспейс кинуть DTO-шку. Так и ладно я мне эти размышления просто замедляют работу. А придёт на проект сотня джуниоров они ведь начнут кидать всё, куда ни попадя потому что Вася у нас художник, Вася так видит. А умудрённых сединами дедов на них на всех не хватит, чтобы в каждом отдельном случае давать ценные указания. Хочу чтобы архитектурный каркас предусматривал чёткие, понятные даже обезьяне правила и форсировал их соблюдение. Можно выпендриться и сказать "формируя у разработчика императив поведения", но я скромный.


Сюда же грануляция. Очень хочу чтобы проблемы с грануляцией (как разложить функциональность по сервисам) мне помогала решать сама архитектура, толкая на правильные решения и выставляя разумные ограничения.


Строгая типизация


Больше строгой типизации! Нам дали в руки компилятор так давайте выжмем из него по-максимуму! Пусть трудится на благо бизнеса. Собрать ваши интерфейсы, написанные по образу и подобию Java-вских, компилятор всегда сможет. Другое дело что пишем-то мы на C# и языковых возможностей у нас куда как больше дженерики вон хорошие, лямбды, разные модификаторы доступа, экстеншны, ковариантность!


Разумеется, я не поддерживаю на голову стукнутых ФП-шников, которые хотят валидировать на этапе компиляции вообще все на свете и стремятся к полному автоматизированному доказательству корректности. Особенно когда они предлагают делать это через стрёмные и непонятные абстракции. Однако, стоит признать за ними определённую правоту и убойную годноту некоторых подходов. Учить новый язык ради этого я, само собой, не стану, но концепции под покровом тихой украинской ночи, перепрячу.


Удобная абстракция от внешних систем


Не хочу писать логику, полагаясь на то, что у нас есть конкретная база данных, конкретная очередь сообщений, конкретный почтовый клиент. А вот чтобы поменять базу (даже сам движок), очередь и почтовый клиент при необходимости можно было без изменений логики. Обычно для таких кренделей нужно самостоятельно выделять нужные интерфейсы и фаршировать ими DI-контейнер. Здесь же мне хочется иметь общие интерфейсы работы заранее, из коробки. В идеале чтобы не делать эту работу заново из проекта в проект.


Проблемы с логикой это одно, а проблемы с внешними системами совсем другое. Большинство багов случается не потому что что-то не так с моей логикой (обычно она упирается в требования), а потому что какая-то внешняя по отношению к моему коду система повела себя не так, как я ожидал. В базе не те данные, веб-запрос падает каждую вторую пятницу каждого третьего месяца, в очередь сообщений дикпики приходят.


Я к тому, что к багам в логике у меня один подход, к багам с внешними системами другой. Хочется чтобы дизайн системы как-то ну я не знаю Эксплицитно обыгрывал этот момент.


Кстати ещё хочу чтобы архитектура хорошо подстраивалась под работу с несколькими базами данных одновременно. Решить, так сказать, проблему абстрактно.


Решение проблем с тестированием


Тесты штука нужная и важная. Не компилятором единым. Мой опыт подсказал, что регрессионного unit-тестирования на кейсах более чем достаточно. Если делать и поддерживать такие тесты будет дёшево, то их можно будет сделать много. А тут уже можно взять количеством. Как говорится, главное завалить а там ногами запинаем.


Категорически необходимо как-то решить проблему с хранением тестовых данных. Тестовая база для разработки у меня есть, конечно же. Но поднимать её в рамках CI-билда категорически не хочется. Да и потом я не хочу тестировать сервер баз данных! Его уже тестируют умные дяденьки за большие деньги. Надо чтобы тестировался мой код, который я написал. А всё остальное не надо.


Вбивать руками тестовые данные, которые уже, блин, лежат в базе тоже не хочу. Разве умная машина не может сделать это сама?


Разумеется, это должны быть честные meaningful-тесты, которые будут падать при критических изменениях в функциональности, сигнализируя что надо поправить. А ещё будет совсем хорошо если на их написание не будет уходить по полдня времени.


Тут ещё вот что надо заметить: как мы разрабатываем? Заводим тестовые данные, что-то пишем, запускаем-дебажим, уточняем требования, снова что-то пишем, снова дебажим Потом в какой-то момент коммитим задачу, пропускаем через QA, мерджим бренч, после чего задача считается решённой. Это логично, все так делают. Так вот, хочется впаять автоматизированное тестирование в этот процесс так, чтобы после приёмки, когда все от QA до бизнеса сказали "да, это работает правильно", настрогать тестов, не меняя функциональность и кинуть в общую кучу, дабы прогонялись каждый билд. Можно даже какой-нибудь code coverage замутить. Меняешь логику видишь что упало. Ну круто же!


А вот TDD на фиг если честно. Невозможно загодя написать тесты для фичи, функциональность которой толком не известна до момента её сдачи. Поэтому ориентироваться на TDD я не стану.


И таким образом...


У меня на руках список пожеланий и хотелок относительно желаемой архитектуры бизнес приложений. Спойлер: я их уже реализовал, статья рассказывающая про реализацию пока что находится в редактуре. Будет неплохо, если в комментариях со мной кто-нибудь подискутирует на архитектурные темы.


За сим откланяюсь, до следующей статьи.


Кому не терпится посмотреть репозиторий тут

Подробнее..

Из песочницы Проектируем мульти-парадигменный язык программирования. Часть 1 Для чего он нужен?

29.09.2020 12:13:11 | Автор: admin
Хабр это замечательное место, где можно смело делиться своими идеями (даже если они и выглядят безумно). Хабр видел много самодельных языков программирования, расскажу и я о своих экспериментах в этой области. Но мой рассказ будет отличаться от остальных. Во-первых, это будет не просто язык программирования, а гибридный язык, сочетающий в себе несколько парадигм программирования. Во-вторых, одна из парадигм будет довольно необычной она будет предназначена для декларативного описания модели предметной области. А в-третьих, сочетание в одном языке декларативных средств моделирования и традиционных объектно-ориентированного или функционального подходов способно породить новый оригинальный стиль программирования онтологически-ориентированное программирование. Я планирую раскрыть в первую очередь теоретические проблемы и вопросы, с которыми я столкнулся, и рассказать не только о результате, но и о процессе создания дизайна такого языка. Будет много обзоров технологий и научных подходов, а также философских рассуждений. Материала очень много, придется разбить его на целую серию статей. Если вас заинтересовала такая масштабная и сложная задача, приготовьтесь к долгому чтению и погружению в мир компьютерной логики и гибридных языков программирования.

Вкратце опишу основную задачу


Она заключается в том, чтобы создать такой язык программирования, который был бы удобен как для описания модели предметной области, так и для работы с ней. Чтобы описание модели было как можно более естественным, понятным для человека и близким к спецификациям программного обеспечения. Но при этом оно должно быть частью кода на полноценном языке программирования. Для этого модель будет иметь форму онтологии и состоять из конкретных фактов, абстрактных понятий и отношений между ними. Факты будут описывать непосредственные знания о предметной области, а понятия и логические отношения между ними ее структуру.

Кроме средств моделирования языку также понадобятся инструменты для подготовки исходных данных для модели, динамического создания ее элементов, обработки результатов запросов к ней, создания тех элементов модели, которые удобней описать в алгоритмическом виде. Все это гораздо удобнее сделать явно описав последовательность вычислений. Например, с помощью ООП или функционального подхода.

Ну и, разумеется, обе части языка должны тесно взаимодействовать между собой и дополнять друг друга. Чтобы их можно было легко сочетать в одном приложении и решать каждый тип задачи с помощью наиболее удобного инструмента.

Свой рассказ я начну с вопроса, зачем вообще создавать такой язык, почему именно гибридный язык и где он был бы полезен. В следующих статьях я планирую сделать краткий обзор технологий и фреймворков, позволяющих совместить декларативный стиль с императивным или функциональным. Далее можно будет провести обзор языков описания онтологий, сформулировать требования и основные принципы нового гибридного языка и в первую очередь его декларативной компоненты. И наконец описать ее основные понятия и элементы. После этого рассмотрим, какие проблемы возникают при совместном использовании декларативной и императивной парадигм и как их можно решить. Также разберем некоторые вопросы реализации языка, например, алгоритм логического вывода. А в конце рассмотрим один из примеров его применения.

Правильный выбор стиля языка программирования важное условие качества кода


Многим из нас приходилось заниматься поддержкой сложных проектов, созданных другими людьми. Хорошо, если в команде есть люди, которые ориентируются в коде проекта и могут объяснить, как он работает, есть документация, код чист и понятен. Но в реальности часто бывает и по-другому авторы кода уволились задолго до того, как вы попали на этот проект, документации или нет совсем или она очень обрывочная и давным-давно устарела, а о бизнес-логике нужного компонента бизнес-аналитик или проект-менеджер может рассказать лишь в общих чертах. В этом случае чистота и понятность кода является критическим показателем.
Качество кода имеет много аспектов, один из них правильный выбор языка программирования, который должен соответствовать решаемой задаче. Чем проще и естественней разработчик может реализовать свои замыслы в коде, тем быстрее он сможет решить задачу и меньше ошибок допустит. Сейчас у нас есть выбор из довольно большого числа парадигм программирования, каждая из которых имеет свою область применения. Например, функциональное программирование предпочтительней для приложений с акцентом на вычисления, так как оно дает больше возможностей для структурирования, комбинирования и повторного использования функций, реализующих операции над данными. Объектно-ориентированное программирование упрощает создание структур из данных и функций за счет инкапсуляции, наследования, полиморфизма. ООП подходит для приложений, ориентированных на данные. Логическое программирование удобно для задач, ориентированных на правила, требующих работы со сложными, рекурсивно-определенными типами данных, такими как деревья и графы, подходит для решения комбинаторных задач. Также свои сферы применения имеют реактивное, событийно-ориентированное, мультиагентное программирование.

Современные языки программирования общего назначения могут поддерживать несколько парадигм. Объединение функциональной и ООП парадигм уже давно стало мейнстримом.

Гибридное функционально-логическое программирование тоже имеет давнюю историю, но за пределы академического мира так и не вышло. Меньше всего внимания уделено объединению логического и императивного ООП программирования (более подробно о них я планирую рассказать в одной из следующих публикаций). Хотя на мой взгляд, логический подход мог бы быть весьма полезным в традиционной сфере применения ООП серверных приложениях корпоративных информационных систем. Просто на него нужно взглянуть немного под другим углом.

Почему я считаю декларативный стиль программирование недооцененным


Попробую обосновать свою точку зрения.

Для этого рассмотрим, что из себя может представлять программное решение. Ее основные компоненты это: клиентская часть (десктоп, мобильные, web приложения); серверная часть (набор отдельных сервисов, микросервисов или монолитное приложение); системы управления данными (реляционные, документо-ориентированные, объектно-ориентированные, графовые базы данных, сервисы кэширования, поисковые индексы). Программному решению приходится взаимодействовать не только с людьми пользователями. Частой задачей является интеграция с внешними сервисами, предоставляющими информацию через API. Так же источниками данных могут быть аудио и видеодокументы, тексты на естественном языке, содержимое web-страниц, журналы событий, медицинские данные, показания датчиков и т. п.

С одной стороны, серверное приложение хранит данные в одной или нескольких базах данных. С другой отвечает на запросы, приходящие от конечных точек API, обрабатывает приходящие сообщения, реагирует на события. Структуры сообщений и запросов почти никогда не совпадают со структурами, хранимыми в базах данных. Форматы входных/выходных данных предназначены для использования извне, оптимизированы под потребителя этой информации и скрывают сложность приложения. Форматы хранимых данных оптимизированы под систему их хранения, например, под реляционную модель данных. Поэтому нужен некоторый промежуточный слой понятий, который позволит совместить между собой вход/выход приложения с системами хранения данных. Обычно этот промежуточный слой называется слоем бизнес-логики и реализует правила и принципы поведения объектов предметной области.

Задача связывания содержимого базы данных с объектами приложения тоже не так проста. Если структура таблиц в хранилище соответствует структуре понятий на уровне приложения, то можно воспользоваться ORM технологией. Но для более сложных случаев чем доступ к записям по первичному ключу и CRUD операций приходится выделить отдельный слой логики работы с базой данных. Обычно схема базы данных имеет максимально общую форму, так, что с ней могут работать разные сервисы. Каждый из которых отображает эту схему данных на свою объектную модель. Структура приложения становится еще более запутанной, если приложение работает не с одним хранилищем данных, а с несколькими, разного типа, загружает данные из сторонних источников, например, через API других сервисов. В этом случае необходимо создать унифицированную модель предметной области и отобразить на нее данные из разных источников.
В некоторых случаях модель предметной области может иметь сложную многоуровневую структуру. Например, при составлении аналитических отчетов одни показатели могут строиться на основе других, которые в свою очередь будут источником для построения третьих и т. д. Также входные данные могут иметь слабоструктурированную форму. Эти данные не имеют строгой схемы как, например, у реляционной модели данных, но все же содержат какую-либо разметку, позволяющую выделить из них полезную информацию. Примерами таких данных могут быть ресурсы семантической паутины, результаты парсинга WEB-страниц, документы, журналы событий, показания датчиков, результаты предварительной обработки неструктурированных данных, таких как тексты, видео и изображения и т.п. Схема данных этих источников будет строиться исключительно на уровне приложения. Там же будет и код, преобразующий исходные данных в объекты бизнес-логики.

Итак, приложение содержит не только алгоритмы и вычисления, но и большой объем информации о структуре модели предметной области структуру ее понятий, их отношения, иерархию, правила построения одних понятий на основе других, правила преобразования понятий между разными слоями приложения и т.п. Когда мы составляем документацию или проект, то мы описываем эту информацию декларативно в виде структур, диаграмм, утверждений, определений, правил, описаний на естественном языке. Нам удобно мыслить таким образом. К сожалению, далеко не всегда эти описания можно выразить в коде таким же естественным образом.

Рассмотрим небольшой пример и порассуждаем, как будет выглядеть его реализация с помощью разных парадигм программирования


Предположим, у нас есть 2 CSV файла. В первом файле:

В первой колонке хранится идентификатор клиента.
Во второй дата.
В третьей выставленная сумма счета,
В четвертой сумма оплаты.

Во втором файле:
В первой колонке хранится идентификатор клиента.
Во второй имя.
В третьей адрес электронной почты.

Введем некоторые определения:
Счет включает в себя идентификатор клиента, дату, выставленную суммы, суммы оплаты и долга из ячеек одной строки файла 1.
Сумма долга это разница между выставленной суммой и суммой оплаты.
Клиент описывается с помощью идентификатора клиента, имени и адреса электронной почты из ячеек одной строки файла 2.
Неоплаченный счет это счет с положительным долгом.
Счета связаны с клиентом по значению идентификатора клиента.
Должник это клиент, у которого существует хотя бы один неоплаченный счет, дата которого старше текущей даты на 1 месяц.
Злостный неплательщик это клиент, имеющий более 3х неоплаченных счетов.

Далее, используя эти определения можно реализовать логику рассылки напоминания всем должникам, передачи данных о злостных неплательщиках коллекторам, расчета пени на сумму долга, составления разнообразных отчетов и т.п.

На языках функционального программирования такая бизнес-логика реализуется с помощью набора структур данных и функций по их трансформации. Причем структуры данных принципиально отделены от функций. В результате модель, а особенно такая ее составляющая как отношения между сущностями, оказывается спрятанной внутри набора функций, размазанной по коду программы. Это создает большой разрыв между декларативным описанием модели и ее программной реализацией и усложняет ее понимание. Особенно если модель имеет большой объем.

Структурирование программы в объектно-ориентированном стиле позволяет смягчить эту проблему. Каждая сущность предметной области представляется объектом, поля данных которого соответствуют атрибутам сущности. А отношения между сущностями реализуются в виде отношений между объектами, частично на основе принципов ООП наследования, абстракции данных и полиморфизма, частично с помощью шаблонов проектирования. Но в большинстве случаев отношения приходится реализовывать, кодируя их в методах объектов. Также кроме создания классов, представляющих сущности, понадобятся также структуры данных для их упорядочения, алгоритмы для заполнения этих структур и поиска информации в них.

В примере с должниками мы можем описать классы, описывающие структуру понятий Счет и Клиент. Но логика создание объектов, связывание объектов счетов и клиентов между собой часто реализуется отдельно в фабричных классах или методах. Для понятий должников и неоплаченных счетов отдельные классы вообще не нужны, их объекты могут быть получены путем фильтрации клиентов и счетов в том месте, где они понадобятся. В результате часть понятий модели будет реализована в виде классов явно, часть неявно, на уровне объектов. Часть отношений между понятиями в методах соответствующих классов, часть отдельно. Реализация модели будет размазана по классам и методам, перемешана с вспомогательной логикой ее хранения, поиска, обработки, преобразования форматов. Для того, чтобы найти эту модель в коде и понять ее, потребуются усилия.

Наиболее близкой к описанию будет реализация концептуальной модели на языках представления знаний. Примерами таких языков являются Prolog, Datalog, OWL, Flora и другие. Об этих языках я планирую рассказать в третьей публикации. В их основе лежит логика первого порядка либо ее фрагменты, например, дескриптивная логика. Эти языки позволяют в декларативном виде задать спецификацию решения задачи, описать структуру моделируемого объекта или явления и ожидаемый результат. А встроенные механизмы поиска автоматически найдут решение, удовлетворяющее заданным условиям. Реализация модели предметной области на таких языках будет исключительно краткой, понятной и близкой к описанию на естественном языке.

Например, реализация задачи с должниками на языке Prolog будет очень близка к определениям из примера. Для этого ячейки таблиц нужно будет представить в виде фактов, а определения из примера в виде правил. Для сопоставления счетов и клиентов достаточно в правиле указать зависимость между ними, а их конкретные значения будут выведены автоматически.

Сначала мы объявляем факты с содержимым таблиц в формате: ID таблицы, строка, колонка, значение:

cell(Table1,1,1,John). 

Затем дадим имена каждой из колонок:

clientId(Row, Value) :- cell(Table1, Row, 1, Value).

После чего можно объединить все колонки в одно понятие:

bill(Row, ClientId, Date, AmountToPay, AmountPaid) :- clientId(Row, ClientId), date(Row, Date), amountToPay(Row, AmountToPay), amountPaid(Row, AmountPaid).unpaidBill(Row, ClientId, Date, AmountToPay, AmountPaid) :- bill(Row, ClientId, Date, AmountToPay, AmountPaid),  AmountToPay >  AmountPaid.debtor(ClientId, Name, Email) :- client(ClientId, Name, Email), unpaidBill(_, ClientId, _, _, _).

И так далее.

Сложности начнутся при работе с моделью: при реализации логики рассылки сообщений, передаче данных другим сервисам, сложных алгоритмических вычислений. Слабым местом Prolog является описание последовательностей действий. Их декларативная реализация даже в простых случаях может выглядеть очень неестественно и требует значительных усилий и квалификации. Кроме того, синтаксис Prolog не очень соответствует объектно-ориентированной модели, и описания сложных составных понятий с большим количеством атрибутов будут довольно тяжелыми для восприятия.

Как же нам совместить основной функциональный или объектно-ориентированный язык разработки с декларативной природой модели предметной области?


Наиболее известным подходом является предметно-ориентированное проектирование (Domain-Driven Design). Эта методология облегчает создание и реализацию сложных моделей предметной области. Она предписывает, чтобы все понятия модели были выражены в коде явным образом в слое бизнес логики. Понятия модели и реализующие их элементы программы должны быть как можно ближе к друг другу и соответствовать единому языку, понятному как программистам, так и экспертам по предметной области.

Богатая модель предметной области для примера с должниками будет дополнительно содержать классы для понятий Неоплаченный счет и Должник, классы-агрегаты для объединения понятий счетов и клиентов, фабрики для создания объектов. Реализация и поддержка такой модели более трудоемкая, а код громоздкий то, что раньше можно было сделать одной строчкой, в богатой модели требует нескольких классов. В итоге, на практике такой подход имеет смысл только при работе больших команд над сложными масштабными моделями.

В некоторых случаях решением может быть комбинация основного функционального или объектно-ориентированного языка программирования и внешней системы представления знаний. Модель предметной области может быть вынесена во внешнюю базу знаний, например, в Prolog или OWL, а результат запросов к ней обработан на уровне приложения. Но такой подход усложняет решение, одни и те же сущности приходится реализовывать на обеих языках, настраивать взаимодействие между ними через API, дополнительно поддерживать систему представления знаний и т.п. Поэтому он оправдан только при большом размере и сложности модели, требующей логического вывода. Для большинства задач это будет избыточным. К тому же, эту модель не всегда можно безболезненно отделить от приложения.

Еще одним из вариантов совмещения баз знаний и ООП приложения является онтологически-ориентированное программирование. В основе этого подхода лежит сходство между средствами описания онтологии и объектной программной моделью. Классы, сущности и атрибуты онтологии, записанные, например, на языке OWL, могут быть автоматически отображены на классы, объекты и их поля объектной модели. А далее полученные классы можно использованы совместно с другими классами приложения. К сожалению, базовая реализация этой идеи будет иметь довольно ограниченные возможности. Языки онтологий довольно выразительны и далеко не все компоненты онтологии могут быть преобразованы в классы ООП простым и естественным образом. Также для реализации полноценного логического вывода недостаточно просто создать набор классов и объектов. Ему требуется информация об элементах онтологии в явном виде, например, в виде мета-классов. Об этом подходе более подробно я планирую рассказать в одной из следующих публикаций.

Есть еще такой экстремальный подход к разработке ПО как разработка, управляемая моделями. Согласно нему главной задачей разработки становится создание моделей предметной области, из которых затем автоматически генерируется код программы. Но на практике такое радикальное решение не всегда обладает достаточной гибкостью, особенно в вопросах производительности программ. Создателю таких моделей приходится совмещать роли как программиста, так и бизнес-аналитика. Поэтому такой подход не смог потеснить традиционные подходы к реализации модели на языках программирования общего назначения.

Все эти подходы довольно тяжеловесны и имеют смысл для моделей большой сложности, часто описанных отдельно от логики их использования. Хотелось бы чего-то более легкого, удобного и естественного. Так, чтобы с помощью одного языка можно было описать как модель в декларативном виде, так и алгоритмы ее использования. Поэтому я и задумался о том, чтобы объединить объектно-ориентированную или функциональную парадигму (назовем ее компонентой вычислений) и декларативную парадигму (назовем ее компонентой моделирования) в рамках одного гибридного языка программирования. На первый взгляд эти парадигмы выглядят противоположными друг другу, но тем интереснее попробовать.

Итак, цель создать язык, который должен быть удобным для концептуального моделирования на основе слабоструктурированных и разрозненных данных. Форма модели должна быть близка к онтологии и состоять из описания сущностей предметной области и отношений между ними. Обе компоненты языка должны тесно интегрированными, в том числе на семантическом уровне.

Элементы онтологии должны быть сущностями языка первого уровня их можно будет передавать функциям в качестве аргументов, присваивать переменным и т.д. Поскольку модель онтология станет одним из основных элементов программы, то такой подход к программированию можно будет назвать онтологически-ориентированным. Совмещение описания модели с алгоритмами ее использования сделало бы программный код более понятным и естественным для человека, сблизило бы его с концептуальной моделью предметной области, упростило бы разработку и поддержку программного обеспечения.

На первый раз достаточно. В следующей публикации я хочу поговорить о некоторых современных технологиях, совмещающих императивный и декларативный стили PL/SQL, Microsoft LINQ и GraphQL. Для тех, кто не хочет ждать выхода всех публикаций на Хабре, есть полный текст в научном стиле на английском языке, доступный по ссылке:
Hybrid Ontology-Oriented Programming for Semi-Structured Data Processing.
Подробнее..

Как выйти на Европейский Рынок эффективно и с минимальными затратами?

30.11.2020 16:22:04 | Автор: admin

Многие успешные компании из Китая и России хотят выйти на рынки ЕС, но не знают как начать или совершают одни и те же ошибки.

На примере одной китайской компании, которую мы успешно продвинули, поделюсь опытом.

Несколько лет назад к нам обратилась китайская компания - производитель компактных видеокамер (action camera, достойные аналоги GoPro, но в разы дешевле) с просьбой обеспечить выход на рынки ЕС.

На тот момент их опыт продаж в ЕС заключался в разовых мелкооптовых поставках B2B через платформу Alibaba или розничных продажах B2C через Aliexpress.

Мы взялись за эту работу и обеспечили за полтора года впечатляющий уровень продаж.

Ниже некоторые полезные выводы:

Страна выхода в ЕС? Эстония

Эстония удобна с точки зрения логистики (особенно для Северной Европы и Германии), отсутствия налога на прибыль, недорогих услугах таможенного хранения и так далее. Ну и мы тут физически находимся.

Изучение рынка? Необязательно

Дорогого и презентабельного не проводили, так как достаточно было изучить таможенную базу импорта в ЕС.

Но при этом учесть еще, что китайские цены, указываемые в транспортных документах, могут быть занижены в разы от реальных (с целью экономии Покупателями ввозных пошлин и НДС).

Объем рынка видеокамер стал понятен и перспективы их продаж тоже.

Бренд? Очень желательно!

Был зарегистрирован в ЕС новый благозвучный бренд на эстонскую компанию.

Бренд и эстонская юрисдикция компании позволили утверждать что видеокамеры имеют европейское происхождение и лишь собираются в Китае (OEM или ODM).

Европейская сертификация СЕ? Обязательно!

Видеокамеры были сертифицированы для использования в ЕС. Собирались сделать в Латвии, но в итоге сертификацию сделали в Китае (там оказалось достаточно профильных лабораторий и недорого).

Транспорт? Авиа - быстро или морем - дешево

Сначала использовали авиадоставку DHL - быстро (несколько дней) но дорого.

Позже, когда стал понятен прогноз продаж, стали отгружать морем (поддоны, сборный груз в контейнере) - долго (месяц) но дешево.

Таможенное хранение? Очень желательно!

Видеокамеры поступали в Таллинне на таможенный склад и хранились там.

При необходимости, растаможивали требуемый объем товара (и только тогда платили пошлины и НСО) и сразу отправляли в место назначения в ЕС.

Каналы продаж? Маркетплейсы и сети магазинов

Опыт показал, что наиболее эффективным каналом продаж стали европейские маркетплейсы с обеспечением доставки (fulfillment services) конечному потребителю (Amazon UK, DE, FR, ES, IT, но не только Amazon).

Далее идет поставка на реализацию в сети магазинов.

Свой сайт оказался менее эффективным чем ожидалось.

Локализация? Есть нюансы

Если Северная Европа (и Германия) английский воспринимают хорошо, то Франция (как и Испания и Италия) предпочитают использовать свои языки. Надо учесть это при локализации продукта через маркетплейсы или онлайн сервисы.

А при физической продаже в магазинах надо обязательно делать перевод на язык страны магазина (вкладываемые инструкции, печать на упаковке или стикеры). Но всё решаемо.

Сервис-центр? Обязательно!

Статистика показала, что 5-10% онлайн продаж были возвращены только потому что Покупатель передумал (ну или попользовал, например, на лыжных каникулах или дайвинге, и больше не надо). Законы ЕС это не запрещают.

Как правило, эти видеокамеры были в рабочем состоянии и требовали переупаковки (или незначительного ремонта типа замены корпуса). Поэтому необходим ЕС-почтовый адрес для приема товара и минимальные площади для переупаковки и с последующей отправкой новому Покупателю.

Неожиданные проблемы? It happens

Например, после аварии самолета из-за возгорания аккумулятора в каком-то электронном изделии, грузовые авиакомпании усложнили требования к перевозке товаров, содержащих аккумуляторные батареи. Были проблемы с приемом к перевозке и у нас.

Что еще оказалось важным? Регистрация плательщика НСО в каждой стране ЕС!

Если склад расположен на территории страны, или продажи в этой стране выше некоторого уровня, то требуется регистрация в налоговых органах этой страны Евросоюза в качестве плательщика НСО.

Процедура сложная, в каждой стране разная, но это надо делать.

Зато факт наличия номера плательщика НСО повышает уровень продаж В2В в этой стране, так как Покупатель зачитывает НСО при покупке.

Есть что ещё сказать? Да

Многое осталось несказанным (мобильные приложения, ПО, дизайн, экспо, реклама, онлайн поддержка, финансирование, отношения с конкурентами и тд).

Если статья окажется интересной читателям - напишу продолжение.

Всем успехов и удачи с выходом на внешние рынки!

Подробнее..

Организация бизнес-логики корпоративных приложений. Какие возможны варианты?

04.05.2021 10:05:07 | Автор: admin

Оригинал статьи находится по адресу

В этой статье мы попытаемся найти ответ на вопрос, обозначений в заголовке. А также порассуждаем на тему возможности универсального решения на все случае жизни.

Три типовых решения при работе с бизнес-логикой по Фаулеру

С одной стороны сложно писать об организации бизнес-логики в приложении. Получается очень абстрактная статья. Благо есть книги, где затронута эта тема и даже есть примеры кода. Мартин Фаулер в книге "Шаблоны корпоративных приложений" выделял три основных типовых решения. Сценарий транзакции (Transaction Script), модуль таблицы (Table Module) и модель предметной области (Domain Model).Самый элементарный из них - это сценарий транзакции. Не будем их здесь обсуждать подробно - они очень хорошо описаны в первоисточнике с примерами. Приведем для дальнейших рассуждений лишь схему все из той же книги:

На этом графике показана _приблизительная_ зависимость между сложностью доменной логики и стоимостью реализации для трех видов типовых решений. Сразу бросается в глаза очевидная схожесть между тем как ведет себя сценарий транзакции и модуль таблицы. И совсем особняком стоит модель предметной области, которую применяют для сложной бизнес-логики. Как выбирать решение для вашего проекта? Очень просто, вы оцениваете насколько сложная будет бизнес-логика. Например расчет скидочной программы для клиентов. Какое типовое решение выбрать? Обычно при расчете скидок пользователи могут быть в разных категориях скидочной программы, в зависимости от категории можно получать скидки на разные группы товаров, причем товары могут входить в иерархические группы. Категории скидок распространяются на категории товаров. А еще есть число посещений заведения за заданный период. Периоды с разными характеристиками, заведения в сети заведений - различаются и т.д. и т.п. Если представить код - это приложение, в котором большое число классов с разнообразными свойствами и большое число связей между этими классами. А также разнообразные стратегии, которые оперируют всеми этими сущностями. В таком случае ответ очевиден - проектирование с использованием модели предметной области позволит вам совладать со всеми сложностями. Другой пример - у вас простое приложение, которое хранит свои данные в 3-х таблицах и никаких особенных операций с ними не делает. Рассылка сообщений по почте - список почтовых ящиков и список отправленных писем. Здесь нет смысла тащить какой-то сложный фреймворк. Простое приложение должно оставаться простым и тут лучше выбрать модуль таблицы или даже сценарий транзакции. В зависимости от того на какой платформе вы собираетесь разрабатывать.

Сколько типовых решений на самом деле?

Похожесть двух кривых объясняется очень просто. Сценарий транзакции и модуль таблицы реализуются преимущественно с использованием процедурной парадигмы программирования. В то время как модель предметной области с использованием объектно-ориентированной парадигмы программирования. Если посмотреть на проблему под этим углом - все становится на свои места.

Понятно, что речь не идет о чистой реализации той или иной парадигмы. Всегда есть компромиссы, вызванные ограничениями используемых технологий. Если вы пишете на C# или Java, объектно-ориентированных языках по своей природе, то это совсем не значит, что ваш код автоматически становится объектно-ориентированным. Всю программу вполне могут поместить в один единственный класс, объявить все методы статическими и использовать их из любого фрагмента кода. Таким образом в каждом конкретном приложении будет своя кривая. Нужно лишь понять к какой из этих двух категорий она ближе.

Как влияют фреймворки и инструменты разработки на кривую стоимости?

Сразу обозначим, что в качестве хранилища данных может выступать не только реляционная СУБД, но и NoSQL хранилище,NewSQL и даже обычные файлы, сериализованные в json, бинарный формат и т.п. Мы смотрим на ситуацию в комплексе. Если говорить про работу с обычными SQL хранилищами, здесь также огромный выбор. Вы можете писать простые запросы, писать хранимые процедуры, можете использовать ORM, использовать Code First, либо DB First подходы - все это в конечном счете сказывается на стиле в котором написана бизнес логика. В большей степени процедурном, либо в большей степени объектно-ориентированном. Ниже я примерно обозначил свое мнение о популярных схемах работы с БД.

Проблема в том, что используемые средства накладывают ограничения, которые не позволят вам реализовать тот или иной подход в полной мере. Например, с помощью Dapper не удобно работать со сложными ассоциациями внутри доменных сущностей. А при использовании ORM уровня Entity Framework вы добавляете код для отображения сущностей на таблицы. Если говорить о NoSQL СУБД, для примера Neo4j, то там очень выразительный и мощный для своих задач язык. Но опять же это приведет к использованию процедурной парадигмы.

Насколько легко сменить выбранное решение?

Давайте попробуем представить ситуацию, где мы решили кардинально изменить схему работы с хранилищем данных. С чем мы можем в таком случае столкнуться? До этого мы обсудили два важнейших аспекта - сложность кода и стоимость его сопровождения. Но на практике этого оказывается мало. Есть еще как минимум вопрос производительности - создаваемое приложение должно быть быстрым. И это сказывается на стиле написания кода. Чем жестче требования производительности, тем более процедурный код мы получаем на выходе. В каком-то экстремальном случае это может быть сервис или приложение, написанное с использованием полностью SQL, где вся логика скрыта в хитрообразных джойнах, оконных функция и обобщенных табличных выражениях. Работает быстро, но перевести его на ORM уже не так просто - все равно что переписать с нуля. Развитие такого продукта также может столкнуться с сложностями, учитывая график выше и процедурный стиль. Еще один вопрос - консистентность данных. Например, реляционные СУБД предоставляют очень богатые возможности по работе с транзакциями. Разобравшись с ними один раз - можно легко писать код, где вы точно знаете какие данные увидит пользователь, какие сможет изменить. С другой стороны, если вы пользуетесь ORM и выносите всю вашу бизнес-логику в классы работать в терминах транзакций становится сложнее. Обычно происходит реорганизация структуры таблиц и даже бизнес-сценариев таким образом, что они начинают работать в стиле согласованности данных в конечном счете (eventual consistency). Очевидно, что это также затрудняет перевод с одной схемы на другую, если вы заранее не заложили такую возможность. Компетенцию команды также не следует сбрасывать со счетов. Часто разработчики знают хорошо либо SQL, либо ORM и при переходе можно неожиданно столкнуться с проблемами.

Выводы

Из всего, что мы обсудили можно сделать выводы:

  • При проектировании сервиса с нуля лучше сразу прогнозировать дальнейшее развитие этого сервиса и выбирать наиболее подходящее типовое решение.

  • Если дальнейшее развитие для сервиса не очевидно, то лучше сразу позаботиться о возможной смене парадигме. Например, предпочитая eventual consistency. При добавлении нового функционала всегда держать в уме возможность смены решения.

  • Для доставшегося в "наследство" программного кода одно из первых действий - это оценка степени соответствия выбранного типового решения и объема уже реализованной логики. Как показывает практика - это наиболее частая причина технического долга.

Какие еще выводы вы могли бы предложить? Оставляйте ваши комментарии, давайте обсудим.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru