Всем привет! Меня зовут Таня, я тимлид группы разработки Axapta в компании Lamoda. В этой статье речь пойдет про разработку нашего первого проекта на платформе Microsoft Dynamics 365 For Finance and Operations.
Я расскажу про подходы, которые мы использовали, про ошибки, которые допускали, поделюсь знаниями и приобретенным опытом. Эта статья может быть интересна тем, кто начинает разработку проекта в D365 или только задумывается об этом.
Это вольная расшифровка доклада с митапа Mycrosoft Dynamics 365 & Power Platform Meetup.
Цель проекта и технические основы
Наш филиал в Германии закупает товары и продает их российскому юридическому лицу. Ранее мы использовали систему Tsenit, которая позволяла вести учет только на уровне финансовых данных, но не справлялась с учетом товара и логистическими задачами. Для решения этих задач у нас были дополнительные инструменты. Данные хранились сразу в нескольких базах. Всё это отрицательно влияло на скорость и надежность всей системы.
Мы хотели, чтобы учетная система помогала немецкому филиалу сдавать отчетность, платить налоги и проходить аудиторские проверки. Прошлая ERP с трудом решала эти задачи, поэтому мы решили разработать и запустить собственную учетную систему. Наша ERP должна была объединить в единый контур финансы, бухгалтерию и логистику филиала. В качестве основного программного обеспечения мы выбрали Microsoft Dynamics 365 — бывшую Dynamics AX, она же Axapta.
Бизнесовая составляющая описана в статье “Технологии, аутсорс и менталитет”. Здесь же будем говорить про техническую реализацию. Итак, нам требовалось автоматизировать несколько бизнес-процессов:
- Закупка товара у поставщиков;
- Продажа российскому юридическому лицу;
- Интеграция между D365 и Ax2012, учетной системой российского юридического лица;
- Автоматизация подбора налоговых схем;
- Сдача отчетности согласно требованиям немецкого законодательства.
В проекте мы решили внедрить облачное решение Microsoft Dynamics 365, поскольку в немецком офисе не было ни IT-инфраструктуры, чтобы развернуть приложение, ни людей, которые бы за нее отвечали. Для небольших удаленных филиалов схема SaaS оптимальна, поскольку позволяет получить все необходимое ПО и среды разработки, чтобы начать внедрение, сразу после подписания договора с провайдером.
У нас были сжатые сроки: необходимо было выполнить всю разработку за 3 месяца. Поскольку в старой системе товарный учет велся в электронных таблицах, перенос всего набора исторических данных был бы неподъемной задачей в середине финансового года. Зато в начале отчетного периода достаточно перенести только балансы. Таким образом, запуск необходимо было произвести либо 1 января 2019 года, либо отложить его еще на год.
Наша команда не имела опыта разработки в D365. Несмотря на все обстоятельства, мы планировали максимально быстро начать этот проект. Дальше я отдельно опишу все этапы разработки. Подробно остановлюсь на каждой итерации: какой опыт мы получили и какие ошибки допустили.
Первая итерация, модификации на версии приложения 7.3
Для того, чтобы быстро приступить к делу, сначала мы разработали простую архитектуру приложений. Она состояла из окружений для разработки – DevBox 1-tier окружения. Все компоненты были установлены на одном сервере/виртуальной машине: Application Object Server (AOS), база данных, Dynamics 365 Retail и Management Reporter.
Для тестирования мы решили использовать SAT окружение – Standard Acceptance Test 2- tier окружение.
2- tier окружение – это Multi-box окружение, компоненты которого устанавливаются в нескольких облачных сервисах и обычно включают в себя более одного Application Object Server (AOS). По сути, оно максимально приближено к продуктивной среде, поэтому мы решили тестировать на нем.
Мы разворачивали первые окружения разработки на имеющейся on-premise инфраструктуре, однако ее мощностей было недостаточно для дальнейшего развития проекта. Поэтому, когда к проекту подключились еще два разработчика, мы быстро и элегантно развернули для них DevBox в облаке.
Управление нашими облачными окружениями происходило через Lifecycle services портал.
Закончив с окружениями, команда приступила к разработке. Мы настроили среды разработки на Visual Studio и подключили их к контролю версий Azure DevOps, предварительно создав ветку для выгрузки кода. Далее мы разработали подход к разработке и переносу изменений на SAT окружение.
В архитектуре D365 нет слоев, весь стандартный код был разложен в модели. Модификации переносились на SAT-окружение через LCS-портал пакетом, содержащим скомпилированную модель.
Модель – это минимальная единица переноса изменений на прод, поэтому мы решили делать одну модель – одну модификацию, чтобы независимо переносить наши модификации для тестирования и далее в прод.
Для начала мы реализовали самую простую и распространенную модификацию – добавление нового поля в стандартную таблицу, его инициализации при создании записи и вывод на стандартную форму.
Даже в таком простом проекте есть новые типы объектов. Мы сделали расширение для добавления новых полей в стандартную таблицу. Для вывода поля на стандартную форму мы сделали новый тип объектов – расширение для формы. А для инициализации поля мы создали класс, который расширяет методы таблицы. Он позволил проинициализировать поле при создании записи.
На такой простой модификации мы увидели один из основных принципов D365 – не изменение, а расширение стандартных объектов.
Следующей модификацией было создание новой формы. Теперь при создании формы требовалось обязательно указывать её паттерн. Паттерн – это шаблон, который полностью определяет структуру дизайна формы. Пока мы полностью не воспроизведем структуру, заложенную в шаблоне, наша форма не будет компилироваться. Изменить шаблон уже готовой формы невозможно. Поэтому перед началом разработки мы заранее продумывали, как будет выглядеть наша форма.
У нас также сохранялась возможность управлять дизайном формы самостоятельно. Если мы указывали pattern – Custom, то полностью сами отвечали за дизайн формы: какие объекты на ней были и с какой вложенностью.
Выводы после первой итерации
1. Не изменять стандарт, а только расширять его.
2. Ссылаться на модель, если используем её объекты в другой модели. Это одно из отличий моделей D365 от предыдущих версий: объект существует только в одной модели.
3.В изменении свойств стандартных объектов есть ограничения. Не все свойства стандартных полей можно изменить в своих расширениях стандартных объектов. Например, расширение таблицы PurchTable – поле LineDisc. Мы можем управлять видимостью поля и меткой, а такие свойства, как обязательность и редактируемость, изменить нельзя.
4. В D365 нет джобов, все делается через классы.
5. Мы слишком мелко побили модели, и оказалось, что наш принцип “одна модификация = одна модель” не работает.
Вторая итерация и переход на одну модель
В начале второй итерации у нас были две модели, которые ссылались друг на друга. Из-за этого мы уже не могли переносить эти модификации независимо. Поэтому мы решили работать в одной новой модели, в которую потребовалось перенести все существующие модификации.
Модель в D365 – это набор исходных файлов, расположенных в отдельной директории. При компиляции они собираются в отдельную библиотеку, имеющую связь с другими библиотеками.
Поэтому слияние в одну модель на DevBox свелось к перекладыванию файлов из одной директории в другую и удалению старых директорий.
Итак, мы сбилдили новую модель, получили ее последнюю версию на каждом DevBox, после чего продолжили работать в рамках одной модели на окружениях разработки.
Естественно, мы уже перенесли пару моделей для тестирования на SAT-окружение. Поэтому надо было удалить их, и зарелизить одну новую.
Все обновления SAT-окружения делались с помощью пакетов, в том числе и удаление моделей. Мы создали пакет с пустыми моделями, которые нужно удалить, и добавили в него скрипт с названиями этих моделей. Далее собрали пакет с новой моделью и накатили на SAT-окружение. Таким образом, SAT обзавелся новой моделью.
Когда объединяли модели, мы заметили, что каждый разработчик называет расширения объектов по-своему. Договорились о правилах наименования объектов по шаблону: PurchTable.LamodaModelFormExtension, PurchTableTypeLamodaModelClass_Extension.
Также мы договорились в команде, что для одного стандартного объекта мы создаем только одно расширение и все в него вносим изменения.
Я выбрала несколько интересных модификаций, которые мы сделали на этом этапе. Они показывают возможные подходы к разработке в D365.
Задача 1
В разноске накладной по заказу на продажу необходимо было подменить номер накладной на номер из заказа. Для этого мы определили стандартный класс с возможностью расширения, который позволил нам выполнить эту модификацию.
Мы сделали расширение стандартного класса SalesInvoiceJourCreate. В его методе getNumAndVoucher() есть Next – это наш новый super, он говорит о вызове стандартного кода метода. После вызова стандартного кода мы заменили номер накладной нужным значением.
Это один из наших подходов разработки: использование расширений и добавление своего кода до или после (как вариант – и до, и после) исполнения стандартного кода.
Задача 2
Нужно было изменить отображение итогов заказа на покупку: сгруппировать итоги по номеру накладной поставщика из строк заказа на покупку. В этом случае мы не нашли места для расширения без падения производительности в два раза, поэтому сделали свой вариант итогов, не трогая стандартные.
Задача 3
Еще одна интересная модификация: в строки формы заказа на покупку нужно было добавить поля из справочника номенклатуры с возможностью фильтрации. В прошлых версиях модификация была совершенно неинтересной и решалась простым добавлением таблицы как datasource на форму и перекрытием двух методов.
В версии 7.3 мы не могли расширять методы на datasource стандартной формы. Чтобы сделать фильтрацию и не создавать для этого новую форму, мы добавили view как datasource на форму.
Возможность расширять методы на datasource появилась в версии 8.1 D365.
Выводы после второй итерации
На этом этапе мы разработали основные модификации, необходимые для запуска проекта.
- Мы ввели правила наименования расширений. Они не только помогли сделать наименования единообразными и понятными, но и в дальнейшем упростили обновление, поскольку наши названия не совпадали с названиями стандартных объектов из пакета обновления.
- Порадовало, насколько быстро происходит построение перекрестных ссылок при билде проекта или модели – очень удобно организовано в этой версии.
- Обновление моделей в разных типах окружения происходит по-разному. Мы убедились в этом на примере слияния моделей.
- Перед началом разработки новой модификации нужно получать последнюю версию модели, поскольку разработка ведется в рамках одной модели.
- Механизм работы data entity для загрузок и выгрузок данных в Excel при обновлениях данных на проде оказался очень удобным. Сейчас наш департамент Data & Analytics использует его для получения данных из нашей облачной D365.
Основную разработку мы сделали в срок. Выход в Go Live состоялся, модель в проде. А перед нами встала проблема релизов только протестированных модификаций в рамках модели. Также нам хотелось облегчить процесс дебага во время тестирования модификаций и ускорить обновление тестового окружения.
Как всё работает теперь
В последней итерации мы добавили два окружения: билд и тест. После того как все окружения были настроены и проверены, мы упростили тестирование и научились релизить только протестированные модификации в рамках модели.
Для тестирования мы развернули 1-tier окружение и подключили его к ветке разработки в системе контроля версий. Обновление теперь состояло из получения последней версии самой модели и ее сборки. В этом окружении мы дебажили, как в обычном DevBox.
Сборка пакетов для релиза теперь осуществлялась на новом билд-окружении. Протестированные модификации переносились в новую ветку в системе контроля версий changeset’ами (пакетами изменений, выгруженными в систему контроля версий), по принципу от самых ранних к последнему.
Дальше мы деплоили пакет на SAT-окружение, где проходило пользовательское тестирование, после чего ставили пакет в расписание на LCS-портале для релиза на прод. Так мы наладили процесс релизов с использованием билд-окружения.
А ещё мы решили ревьюить не проекты, а changeset’ы по модификации, выгруженные в контроль версий.
Первое обновление облачной версии
Мы работали на облачной версии, поэтому нам необходимо было регулярно обновляться. Первое обновление было переходом с версии 7.3 на версию 8.0. Оно заняло около двух недель.
Основные проблемы мы, конечно, создали себе сами, но и победили тоже сами:
- Мы не сразу договорились о правилах наименования стандартных объектов. В первом же обновлении наши названия объектов совпали с названиями объектов в пакете обновления.
- При обновлении облачных окружений мы обязательно разлогинивались из AOS-машин, иначе процесс обновления не мог завершиться при залогиненом пользователе.
- Пакет обновлений для прод и SAT окружений необходимо было объединять с пакетом модели.
На сегодня обновление всех наших окружений занимает около 3-4 дней и проходит без привлечения разработчиков. Мы можем даже выпустить релиз одновременно с обновлением, главное, чтобы билд, SAT и прод имели одну версию.
Процесс обновления состоит из скачивания на lcs портале пакета обновления. Первыми обновляются DevBox и тест, далее обновляется билд, последними идут SAT и прод.
Итоги всего первого проекта
- Мы получили опыт в построении архитектуры приложений D365.
- Разработали новый подход к код ревью.
- Сделали регламент переноса баз на DevBox (в D365 важно проводить первичное тестирование на DevBox, а сейчас мы даже тестируем на DevBox разработчиков).
- Написали регламент по ведению разработки в D365.
- Научились разрабатывать в облачной среде.
Весь этот опыт помог нам развивать дальше проект более продуманно. Теперь мы знаем возможности системы, можем корректнее выстраивать архитектуру, точнее ставить задачи. Выстроенные процессы вокруг проекта позволяют достаточно просто подключать к нему разработчиков, которые впервые пишут под D365.
Автор: TatyanaGorgul