Post-EngineeringJekyll2015-03-24T18:57:20+00:00/Post-Engineering//blog/no-way-protobuf2015-03-24T00:00:00+00:002015-03-24T00:00:00+00:00Maksimcornelius@thewhip.com<p>Protobuf не взлетел.<br />
Я воображал, как здорово было бы завести отдельный репозиторий, в котором хранились бы только схемы моделей, собирать их в отдельный артефакт и подключать как зависимость во все взаимодействующие приложения. Серверная часть генерировала бы настоящие — гугловые — протобафные классы, андроидный клиент — свои, на Wire.<br />
Начав с конца и успев наесться других проблем, я всё-таки дошёл до того момента, когда наивный воображатель обнаруживает отсутствие мавеновских плагинов для протобафа, просто работающих из коробки. И это стало последным фактором против использования protobuf.<br />
Дальше — JAXB. </p>
<p><a href="/blog/no-way-protobuf/">Protobuf не взлетел</a> was originally published by Maksim at <a href="">Post-Engineering</a> on March 24, 2015.</p>/blog/android-asynctask2015-03-22T00:00:00+00:002015-03-22T00:00:00+00:00Maksimcornelius@thewhip.com<p>Использовать AsyncTask’и для выполнения HTTP-запросов — это просто космос!<br />
Затрудняюсь даже подобрать подходящую аналогию подобному неуместному использованию технологий. Добавьте сюда (странные?) ограничения, например, что создавать таски можно только из UI-потока, и только из него же нужно вызывать <code>execute(Params…)</code>, и что получится? Ах да, ведь начиная с 3.0 версии андроида AsyncTask’и по умолчанию выполняются в одном background-потоке, так что об одновременном запросе сразу нескольких ресурсов можно забыть.<br />
Я не говорю, что AsyncTask’и плохие, я хочу сказать, что по умолчанию выбирать для работы с IO именно их — странная рекомендация. </p>
<p><a href="/blog/android-asynctask/">Android AsyncTasks</a> was originally published by Maksim at <a href="">Post-Engineering</a> on March 22, 2015.</p>/blog/android-injector2015-03-19T00:00:00+00:002015-03-19T00:00:00+00:00Maksimcornelius@thewhip.com<p>Возможно потому, что под Андроид нужно писать на джавке, так хочется притащить в андроидное приложение некоторые ставшие привычными вещи. Например, хочется иметь возможность попросить заинжектить все зависимости компонента. Нет, конечно, хочется, чтобы этого даже просить не нужно было, но в андроидной инфраструктуре это будто бы невозможно. </p>
<p>Единственный [полиморфный] контекст до которого в Андроиде могут дотянуться все компоненты, участвующие в обычном жизненном цикле, — это <code>Application</code>. С другой стороны, завязывать все-все-все компоненты на какой-то конкретный апп (и пораждать циклические зависимости) нет никакого желания, и на свет просится интерфейс <code>Injector</code> с единственным методом <code>void inject(Object o)</code>. Более того, этот тощий интерфейс даже заселяется в отдельный, специально для него созданный, модуль, за что на работе меня некоторые мои коллеги бы прокляли.<br />
Сам же Application использует Dagger от Square, подробнее о котором можно <a href="http://square.github.io/dagger/">тут</a> почитать. </p>
<p>Даггер непривычен тем, что в нём при описании модулей нужно перечислять все классы, которым будут предоставляться зависимости. Каждый раз, создав новый фрагмент и попробовав вызвать <code>.inject(this)</code> из метода <code>onCreate</code>, прийдётся встречаться с сообщением об ошибке (времени исполнения, конечно же), добавлять фрагмент в список <code>injects</code> одного из модулей даггера и пересобирать приложение. </p>
<p><a href="/blog/android-injector/">Android Injector</a> was originally published by Maksim at <a href="">Post-Engineering</a> on March 19, 2015.</p>/blog/client-server-interaction2015-03-18T00:00:00+00:002015-03-18T00:00:00+00:00Maksimcornelius@thewhip.com<p>Пришло время заняться серверной частью и клиент-серверным взаимодействием.<br />
На бэкэнде всё почти обычно: Java-приложение, Spring Boot, Jackson, в ближайшем будущем добавится Hibernate.<br />
Довольно странным получился Maven-layout приложения. Предполагается, что новые функциональные возможности (features) будут проходить сквозь все слои приложения (по сути являясь под-приложениями), все они будут наследоваться от абстрактного Maven-проекта Feature, и подключаться к основному приложению будут как <code>runtime</code>-зависимости. </p>
<p>Андроидная часть реализована отвратительно. Сейчас получилось, что я рассчитываю, что во время обработки ответа фрагмент для аутентификации всё ещё отображается, что активити для этого фрагмента всё ещё существует. Это повод для будущих улучшений; когда таких фрагментов наберется 3-4, станет возможным выделить их общие проблемы и предложить какой-то паттерн для их решения (а может быть кто-нибудь подскажет что-нибудь умное к тому моменту). Нагромождение вырожденных <code>AsyncTask</code>ов усилилось, добавился <code>ValidateTokenTask</code>. Общение с игровым сервером реализовано хардкорно — через <code>HttpURLConnection</code>. Классы, описывающие ответы сервера, дублируются (и могут привести к сложно выявляемым ошибкам, если забыть скопировать изменения между проектами).<br />
Изначально я думал, что можно было бы попробовать вообще не описывать игровые сущности как Java-классы самому, а генерировать их по какой-нибудь схеме (и делать это как при сборке сервера, так и для клиента); Hibernate тоже можно было бы подключить через XML-конфигурацию. Но всё-таки между тем, с чем удобно работать на сервисном уровне, и объектами, пригодными для клиент-серверного общения, нередко целая пропасть. </p>
<p>Серверная проверка корректности гугл-токена и получение информации о пользователе тоже сделаны хардкорно, тоже через <code>HttpURLConnection</code>. Возможно, если когда-нибудь на бэкэнде появится необходимость более тесной интеграции с сервисами Гугла, то и получение информации о пользователе можно будет заменить на библиотечный.<br />
Сложным оказался вопрос о том, что возвращать клиенту в случае успешной аутентификации. Поэтому пока что возвращается только результат: <code>success</code>/<code>fail</code>/<code>error</code>. </p>
<p>Удостоверившись, что весь цикл аутентификации работает, я отправил изменения в соответствующие ветки и решил немного заняться инфраструктурными вещами в андроидной части игры. </p>
<p><a href="/blog/client-server-interaction/">Клиент-серверное взаимодействие в Android-приложении</a> was originally published by Maksim at <a href="">Post-Engineering</a> on March 18, 2015.</p>/blog/Google-login2015-03-12T00:00:00+00:002015-03-12T00:00:00+00:00Maksimcornelius@thewhip.com<p>Как проще всего сделать аутентификацию в приложении под Андроид? Использовать Google аккаунт!<br />
На самом деле нет.<br />
Если вы знаете что делать, то простая форма с логином и паролем обойдется меньшей кровью.<br />
В Android Studio есть опция создания activity для аутентификации через Google+, но генерируется настолько устаревшая форма для логина, что результат даже отказывается компилироваться. </p>
<p>В первую очередь, ещё до того как приступить к написанию кодулек, нам понадобится зарегистрировать наше приложение в <a href="https://code.google.com/apis/console/">Google APIs Console</a>, а для этого нужен SHA ключа, которым подписано приложение. Для debug keystore можно выполнить команду
<code>keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v</code> (пароль android).<br />
Также <strong>обязательно</strong> нужно во вкладке Consent screen выбрать Email address (на обнаружение этой ошибки ушло 20 минут моих нервов).<br />
Если приложение не зарегистрировать, <code>GoogleAuthUtil.getToken</code> будет возвращать <code>null</code>. </p>
<p>Код получения токена я практически полностью скопировал из примера, однако оставлять его в таком состоянии посчитал невозможным. Statefull логику хорошо бы сразу перенести во фрагмент, переживающий пересоздание активити (например, при повороте телефона).<br />
Сигнатура <code>AsyncTask<Void, Void, Void></code> явно намекает на то, что <code>AsyncTask</code> здесь использован не по назначению, но это повод для будущих изменений.</p>
<p><a href="/blog/Google-login/">Google-аутентификация в андроид-приложении</a> was originally published by Maksim at <a href="">Post-Engineering</a> on March 12, 2015.</p>/blog/graddle2015-03-10T00:00:00+00:002015-03-10T00:00:00+00:00Maksimcornelius@thewhip.com<p>Идея ориентироваться только на пятый Андроид на фоне статистики использования выглядит не очень радужной: сейчас устройств с Андроидом пятой версии меньше процента.<br />
Jelly Bean (4.1) с его 82% смотрится гораздо лучше.</p>
<p>В качестве lifecycle management инструмента я решил впервые попробовать Gradle вместо привычного Maven. Наверное, если в разработке проекта делается упор на скорость выхода в маркет, этого делать и не стоило.<br />
За более компактный синтаксис приходится платить. Например, в строке с указанием зависимости Android Studio не подскажет вам доступные версии артефакта. В репозитории с проектом появляется мусор, необходимый Gradle для работы, включая .jar-файл.<br />
Но все неудобства и непривычности должны в будущем компенсироваться, если верить <a href="http://tools.android.com/tech-docs/new-build-system/user-guide">Gradle Plugin User Guide</a>.</p>
<p><a href="/blog/graddle/">Версия Android и сборка Gradle</a> was originally published by Maksim at <a href="">Post-Engineering</a> on March 10, 2015.</p>/blog/starting-android-game2015-03-09T00:00:00+00:002015-03-09T00:00:00+00:00Maksimcornelius@thewhip.com<p>Хочется сделать что-то похожее на форматные (кулонные) бои в Бойцовском Клубе.<br />
Приложение для Андроида с минимально возможной игровой инфраструктурой: нет магазинов, нет городов, нет чата, вообще ничего нет. В первой версии нельзя будет даже выбрать архетип персонажа (уворот/крит). Нет заявок на бои: в первой версии все бои проходят в формате 2 vs 2, команды формируются автоматически.<br />
Опять же в целях упрощения, приложение под последний Андроид, никакого pixel-perfect. С любовью к библиотекам от Square.<br />
На бэкэнде Спринг, вероятнее всего Спринг Бут. Для клиент-серверного взаимодействия в первую очередь попробую Atmosphere. Интеграция через STOMP over WebSocket в данном случае выглядит слишком сложной; клиент подписывается сразу на все события для своего персонажа и смысла в нескольких эндпоинтах нет.<br />
Следить за развитием событий можно по тегу androgame.</p>
<p><a href="/blog/starting-android-game/">Кулонные бои под Андроид</a> was originally published by Maksim at <a href="">Post-Engineering</a> on March 09, 2015.</p>/blog/docker-dns2014-10-01T00:00:00+00:002014-10-01T00:00:00+00:00Denis Golovachevdenis@borov.net<p>Сегодня расскажу как поднять свой DNS сервер в Docker контейнере.<br />
Уже почти год как сам пользуюсь таким в <a href="https://www.digitalocean.com/?refcode=06c4ef09e5cc">Digital Ocean</a>. Приватной сети у данного провайдера все еще нет. Поэтому все мои машинки соединены через OpenVPN и в этот же VPN прокинут DNS сервер, через который осуществляется service location. О такой конфигурации и пойдет речь.</p>
<h2 id="section">Собираем образ</h2>
<p>Cоберем образ сервера с помощью Docker. </p>
<pre><code class="language-bash">## Dockerfile
FROM ubuntu
MAINTAINER Denis Golovachev
RUN apt-get update
RUN apt-get install -y bind9
CMD usr/sbin/named -c /etc/bind/named.conf -f
</code></pre>
<pre><code class="language-bash">docker build -t server/bind .
</code></pre>
<p>И заберем из контейнера папку с конфигурацией DNS сервера.</p>
<pre><code class="language-bash">bindId=$(docker run -d serer/bind)
docker cp $bindID:/etc/bind ./bind
docker stop $bindID
</code></pre>
<p>Теперь мы можем отредактировать конфигурацию DNS сервера</p>
<h2 id="dns-server">DNS Server</h2>
<p>Добавим своих DNS записей. Для этого создадим свою доменную зону <code>.tapcat</code></p>
<pre><code class="language-bash"># bind/named.conf.local - Добавим запись вида
include "/etc/bind/named.conf.tapcat";
# bind/named.conf.tapcat - Создадим файл
zone "tapcat" {
type master;
file "/etc/bind/db.tapcat";
};
# bind/db.tapcat - Создадим файл. За доп опциями можно обратиться в man bind
$TTL 10
$ORIGIN tapcat.
@ IN SOA ns1.tapcat hostmaster.tapcat. (
2014040901 ; Serial
2h ; Refresh
1h ; Retry
1w ; Expire
2h ; Negative Cache TTL
)
@ IN NS ns1. ; Master
ns1.tapcat. IN A 10.8.0.19
master.tapcat. IN A 10.8.0.19
cname.tapcat. IN CNAME master.tapcat.
cname2.tapcat. IN CNAME master.tapcat.
</code></pre>
<p>Запускаем с нашей конфигурацией</p>
<pre><code class="language-bash">docker run -d -p 53:53/udp -v /home/postengineering/bind9/conf:/etc/bind -c 25 -m 30m server/bind
</code></pre>
<p>Готово. У нас теперь свой ДНС сервер. Давайте прокинем его в OpenVPN. </p>
<h2 id="openvpn">OpenVPN</h2>
<p>Машина, на которой установлен bind должна находиться в VPN сети. После этого мы редактируем конфиг VPN сервера</p>
<pre><code class="language-bash"># /etc/openvpn/server.conf - добавим строчку
push "dhcp-option DNS 10.8.0.3"
# где 10.8.0.3 - адрес машины с VPN сервером
</code></pre>
<p>Изменяем конфигурацию наших VPN клиентов</p>
<pre><code class="language-bash"># etc/openvpn/client.conf - добавляем
up /etc/openvpn/update-resolv-conf
down /etc/openvpn/update-resolv-conf
</code></pre>
<p>И кладем данный update-resolv-conf рядышком. Сам скрипт лежит у вас в репозитории пакетов или гуглится.<br />
Перезапускаем bind:</p>
<pre><code class="language-bash">docker run -d -p $VPN_IP:53:53/udp -v /home/postengineering/bind9/conf:/etc/bind -c 25 -m 30m server/bind
</code></pre>
<p>Готово. Теперь в нашей внутренней сети есть свой маленький DNS сервер.</p>
<p><a href="/blog/docker-dns/">DNS внутри контейнера</a> was originally published by Denis Golovachev at <a href="">Post-Engineering</a> on October 01, 2014.</p>/blog/month-with-haskell2014-09-28T00:00:00+00:002014-09-28T00:00:00+00:00Denis Golovachevdenis@borov.net<p>Вот и заканчивается месяц Хаскеля.<br />
Бинарники, библиотеки, линковка.
Писали на Хаскеле веб. Сейчас же на всем пишут веб! Удалось познакомиться с двумя веб фреймворками: Yesod и Snap Framework.</p>
<h2 id="snap-framework">Snap Framework</h2>
<p>Такой, аля Sinatra для Хаскеля. Простенький, приятный. Однако, чтобы написать что-нибудь сложнее чем hello-world - потребует от вас написать пару плагинов. Готовых компонентов мало. Молодой фреймворк.</p>
<h2 id="yesod">Yesod</h2>
<p class="pull-right"><img src="/images/yesod.png" alt="Yesod logo" /></p>
<p>Похож на Django. Очень много кодогенерации. ORM, JS, CSS, HTML, JSON Types - все это генерится. Изменил шаблон главной страницы - попал на перекомпиляцию. Очень грустно писать JS/CSS.<br />
А в остальном - ВЕЩЬ. Все, что хочешь: авторизации, шаблоны, маппинг параметров, typesafe url, тесты. Кстати, о тестах. </p>
<h2 id="tests">Tests</h2>
<p>Что-то не очень в Хаскель сообществе принято тесты писать. А я люблю тесты. С Yesod ситуация приемлимая. Можно написать тесты для JSON Rest. Присутствуют селекторы для построения HTML ассертов. Напрягает вывод при падении теста. Ну не пишет Yesod что сломалось. Пишет, что завалился. А разобраться почему - отдельный квест.
К тестам обязательно нужен continuous integration. Тут засада. Дня 2 разбирался. Во первых - Yesod собирается минут 40 у меня локально на CoreI5. На тревисе это время можно легко умножить на 2. Да еще и билд тревиса постоянно норовит упасть со странными ошибками.<br />
В итоге собирали на тревисе Docker образ из предзаготовленного, с установленным Yesod. Кстати, образ доступен на Docker Hub <a href="https://registry.hub.docker.com/u/wonderbeat/yesod/">тыц</a>.</p>
<h2 id="deploy">Deploy</h2>
<p>Опять же, не JVM. Бинарник собирается под платформу. Кросскомпиляцию делать лень. С тревиса забирать - странная затея. Опять помог Docker с предзаготовленным Yesod.<br />
Памяти жрет много. Сравнимо с JVM. Даже, иногда больше. Но это уже проблема кривых рук. Ведь в Хаскеле, по умолчанию, используется lazy-evaluation. Написал красивую рекурсивную функцию, а по памяти просел мегабайт на 10. Следить надо. А следить - сложно.</p>
<h2 id="development">Development</h2>
<p>Разработка на Хаскеле легко может напугать ребят из мира Java/C#. Тут все очень похоже на С++. Компилятор чего-то постоянно собирает. Странные, ничего не говорящие ошибки. Циклические импорты. Линковка.<br />
Все плохо с IDE. Почитав интернеты, я остановился на SublimeHaskell. Автокомплит. Тесты запускаются автоматически. Но, автокомплит глупый-глупый. Можно узнать тип используемой функции. Опять же - не всегда.</p>
<p>Хаскель - вещь классная. Огорчает инфраструктура. Очень похоже на ситуацию с NPM. Полно пакетов. Документации ноль. Как настраивать - не понятно. Сырые инструменты, велосипеды.<br />
Надо попробовать написать что-нибудь без веба. Пойдемте на хакатон?</p>
<p><a href="/blog/month-with-haskell/">Месяц с Хаскелем</a> was originally published by Denis Golovachev at <a href="">Post-Engineering</a> on September 28, 2014.</p>/blog/haskell-hackathon2014-09-01T00:00:00+00:002014-09-01T00:00:00+00:00Denis Golovachevdenis@borov.net<p>Астрологи объявили месяц изучения Хаскеля!
Запускаем хакатон.
Таймслот - месяц.
Задача - выпустить приложение под Иос/Андроид с веб сервером.
Код тут - <a href="https://github.com/TimeAttack">GitHub</a>.<br />
Полетели!</p>
<p><a href="/blog/haskell-hackathon/">Haskell hackathon</a> was originally published by Denis Golovachev at <a href="">Post-Engineering</a> on September 01, 2014.</p>/blog/space-marines2014-08-25T00:00:00+00:002014-08-25T00:00:00+00:00Denis Golovachevdenis@borov.net<p>У нас в компании прошел турнир по вот такой пошаговой игре, где программисты должны написать
алгоритм для захвата галлактики дронами.
Таких игр довольно много, ага. Однако, прямо во время анонса турнира, в Лас-Вегасе проходил DEFCON 22, где я засек очень интересный доклад <a href="https://www.defcon.org/html/defcon-22/dc-22-speakers.html">I am a legend: Hacking Hearthstone with machine learning</a>
В голове зародилась идея. А вдруг можно написать бота для этой игры с использованием machine learning и без опыта его использования.</p>
<h1 id="section">Прототип</h1>
<p>Прототип собрали за 4 дня. Писали на Kotlin с использованием библиотеки <a href="https://mahout.apache.org/">Mahout</a>.
Жаль что библиотека скудна на примеры и приходилось очень много вычитывать в мейл-переписках коммьюнити.
После того, как собрали базу в 20 мб игр, оказалось что бот чему-то научился. Он захватывал соседнюю планету (выход в галлактику). Загружал ее дронами “выше крыши” и отсижывался. Такая тактика позволяла ему занимать не ниже второго места в тренировках.
Но, времени уже не было и с этим крысоботом мы пошли на полуфинальные игры</p>
<h1 id="section-1">Полуфинал</h1>
<p>И прошли. Очень глупо, но прошли. Во время трансляции нашей игры коментаторы постоянно думали, что наш бот отвалился и не играет. А это была его тактика ;).
Бота решили подтюнить.</p>
<h1 id="section-2">Финал</h1>
<p>На финал мы решили схитрить. Первые ходы в каждой игре делал алгоритм коллеги, который не прошел в финал. После этого машин лернинг брал управление на себя. Посмотрев, как дело обстоит на тренировке, мы увидели, что машина начинает нападать при такой завязке. И выигрывает. Все тренировочные бои такая машина выиграла.
Но, проиграла турнир. Играла не очень. И тут не понятно почему. Толи ошибка у нас в программе, толи базу собрали маленькую, толи алгоритмы соседских врагов были направлены на ближайшего соседа - нашего бота.
Но, это не важно. У нас получился очень интересный бот. Бот, следующий ход которого не могут предсказать даже разработчики. И это здорово!</p>
<p>Код нашего бота лежит на гитхабе - <a href="https://github.com/WonderBeat/suchmarines">вот тут</a></p>
<p><a href="/blog/space-marines/">Machine-learning bot</a> was originally published by Denis Golovachev at <a href="">Post-Engineering</a> on August 25, 2014.</p>/blog/4-digit-pins2014-04-18T00:00:00+00:002014-04-18T00:00:00+00:00Denis Golovachevdenis@borov.net<p>Завалялась у меня коллекция четырехзначных пинов платежных операций. Делюсью
Коллекция содержит уникальные пользовательские пины отсортированные по популярности.
Статистика собрана на основе дампа десяти тысяч пинов.
Может кому и пригодится.
Откуда - секрет ;)</p>
<p><a href="https://gist.github.com/WonderBeat/11028348">4 digit PINS</a></p>
<p><a href="/blog/4-digit-pins/">4 Digit Pins collection</a> was originally published by Denis Golovachev at <a href="">Post-Engineering</a> on April 18, 2014.</p>/blog/big-red-button2014-02-10T00:00:00+00:002014-02-10T00:00:00+00:00Denis Golovachevdenis@borov.net<p>Раньше я только подозревал, но сейчас уже полностью уверен, что аутсорс - унылое говно. А помогла мне вот такая история.</p>
<p>Месяц назад нашей команде прилетела задача. Баг. На мобильном приложении не отображается кнопка. Кнопка красная. Важная. С ее помощью можно было посмотреть различные конфигурации товара. Например, комплектации выбранного вами ноутбука.</p>
<p>Фигня делов, сказал девелопер и взялся за работу. Сравнив XML дамп старого и нового бекенда, а задача у нас именно поддержать мобильное приложение при переезде на новый бекенд, выяснили, что не приходит два дополнительных поля. Добавили за несколько дней (аутсорс ведь ;) ).</p>
<p>Следующий шаг задачи - тестирование. “Где можно увидеть эту кнопку?” - спросил тестировщик. “Ой, не знаю где у вас на тестовом окружении она может появиться.” - был ответ. Ну да ладно. Через 2 дня задача закрылась.</p>
<p>Но, самое интересное произошло на демо. Демонстрация задачи заказчику провалилась. Заказчик решил нажать на кнопку! И приложение закрылось. Раз… и закрылось. “Ой” - сказал девелопер. “Ой” - сказал тестировщик. Никто не нажал на красную кнопку до демо.</p>
<p>И это тут везде. Стремление к тому, чтобы закрыть задачу вытеснело здравый смысл. Никто даже не задумывается. Закрывай таски - получай деньги.</p>
<p>Но, я все еще хочу это исправить.</p>
<p><a href="/blog/big-red-button/">Большая красная кнопка</a> was originally published by Denis Golovachev at <a href="">Post-Engineering</a> on February 10, 2014.</p>/blog/fewer-buttons2013-11-14T00:00:00+00:002013-11-14T00:00:00+00:00Maksimcornelius@thewhip.com<p>В детстве у меня была Sega, а у моего товарища был самый настоящий компьютер. Я был маленький и завидовал, что у него большая клавиатура, на которой так много кнопок.
Потом мы как-то раз попробовали у него дома поиграть в Mortal Combat. Вдвоем на этой прекрасной клавиатуре попасть в нужные кнопки оказалось довольно непростым занятием, да ещё под неестественным углом запястья выворачивать приходилось.
Делайте меньше кнопок, пожалуйста.</p>
<p><a href="/blog/fewer-buttons/">Меньше кнопок</a> was originally published by Maksim at <a href="">Post-Engineering</a> on November 14, 2013.</p>/blog/domain2013-11-01T00:00:00+00:002013-11-01T00:00:00+00:00Maksimcornelius@thewhip.com<blockquote>
<p>Уровень предметной области (Domain Layer) или Уровень модели (Model Layer).
Отвечает за представление понятий прикладной предметной области, рабочие состояния, деловые регламенты.
Именно здесь контролируется и используется текущее состояние прикладной модели, пусть даже технические подробности манипуляции данными делегируются инфраструктуре.
<em>Этот уровень является главной, алгоритмической частью программы.</em>
Domain-Driven Design</p>
</blockquote>
<p>Почему же я всё чаще вижу как создаются приложения, где есть только “entity” и “dto”?
В какой момент на вопрос о том, зачем нужен какой-то класс-сервис, стало нормой отвечать, что он нужен “для работы с энтити”?
Вопросы риторические.</p>
<p><a href="/blog/domain/">Домен</a> was originally published by Maksim at <a href="">Post-Engineering</a> on November 01, 2013.</p>/blog/simple-webhook-handler2013-10-23T00:00:00+00:002013-10-23T00:00:00+00:00Denis Golovachevdenis@borov.net<p>Решил попробовать запустить деплой по нотификации от CI. Travis умеет дергать урлы при удачном билде. Осталось только среагировать на это.</p>
<p>Для реагирования нужна програмка. Програмка простая. Висит, слушает порт, а по сигналу запускает скрипт.
Пошарив на гитхабе Я обнаружил маленькую тележку таких програм. Но, чаще всего они были заточены на определенные сервисы (Travis, Github), и для их запуска
было необходимо снабдить машину Python-ом или NodJS-ом.
Ставить NodeJS ради WebHook хендлера, запускающего скрипт!? Не, не мой вариант. Точно. Мы сделаем это “Linux Way”</p>
<p>Итак.</p>
<ul>
<li>Задача: запустить скрипт после того, как нам придет event от CI [HTTP GET]</li>
<li>K.I.S.S.</li>
</ul>
<p>Нам нужен сервер? Возьмем могучий netcat! В большинстве дистрибутивов он идет в комплекте.</p>
<p><code>
while true; do
echo "Deploy starts NOW!" | nc -l -p 1500 -q 0;
./deploy.sh
sleep 1m;
done
</code></p>
<p>Вот так. В несколько строчек.</p>
<p><a href="/blog/simple-webhook-handler/">Webhook handler with netcat</a> was originally published by Denis Golovachev at <a href="">Post-Engineering</a> on October 23, 2013.</p>/blog/cors-and-thirdparty-cookies2013-10-17T00:00:00+00:002013-10-17T00:00:00+00:00Denis Golovachevdenis@borov.net<p>Всегда хотел написать веб приложение в котором клиент полностью отделен от сервера, находится на другом домене и с сервером только по REST общается. Встрял на одной проблеме. Довольно интересной.</p>
<p>Решил Я попробовать сделать следущую архитектуру. Веб приложение находится на основном домене, а сервер с логикой располагается на другом.
Технологии позволяют: <a href="https://en.wikipedia.org/wiki/Cross-Origin_Resource_Sharing">Cross-Origin-Resource-Sharing</a> уже в браузерах.
Механизм такой: приложение запрашивает сервер на другом домене, а он отвечает с заголовками</p>
<p><code>
Access-Control-Allow-Origin: http://your-webapp.com
</code></p>
<p>Браузер не ругается. Полет нормальный.</p>
<p>Следующий этап приложения - это авторизация. Как мы знаем, в качестве идентификатора пользователся в вебе используются Cookies. И, вроде они нам подходят.
Согласно спецификации (<a href="http://www.w3.org/TR/cors/">link</a>), для использования идентификаторов (credentials) при работе с CORS, нам нужно сделать</p>
<pre><code class="language-bash">invocation.open('GET', url, true);
invocation.withCredentials = true; # выставить флаг на клиенте
invocation.onreadystatechange = handler;
invocation.send();
</code></pre>
<p><code>
Access-Control-Allow-Credentials: true # добавить заголовок в ответ на сервере
</code></p>
<p>С этого момента становится возможным передача Cookie / Set-Cookie заголовков. В ином случае их отрежет браузер.
Теперь должно работать. И, даже работает. Но не везде. Далеко не везде.</p>
<h2 id="third-party-cookies">Third-Party Cookies</h2>
<p>Куки третьей стороны ;) <a href="https://en.wikipedia.org/wiki/HTTP_cookie">link</a>.
В двух словах: браузер позволяет установить куки для домена, отличного от домена в адресной строке. Это может быть сделано, в результате запроса картинки/скрипта.
TP Cookies используются компаниями, которые хотят следить за пользователями в интернетах.
И да-да. Кнопка “+1” и “like” тоже следит за нами. Именно с помощью third-party-cookies. А потом удивляешься, почему после того, как ты посмотрел сайт про ремонт, facebook начал тебя объявлениями тематическими заваливать.</p>
<h2 id="intersection">Intersection</h2>
<p>Так что же произойдет, если мы будем использовать CORS при включенной блокировке Third-Party Cookies?
Я попробовал. И браузер отказался принимать Cookies. Вот она - проблема!</p>
<h2 id="firefox--safary">Firefox && Safary</h2>
<p>По умолчанию, Third-Party-Cookies запрещены в Firefox & Safary.
Как же без этого работают встраиваемые виджеты!? Например, виджет Disqus, который можно наблюдать под статьей.
Ха-ха. А ведь он не работает. Точнее, работает, но не полностью. Не работает OAuth авторизация. Похоже, Disqus об этом знает (<a href="http://help.disqus.com/customer/portal/articles/466235-enabling-cookies">link</a>), но ничего сделать не может.</p>
<h2 id="section">Пичаль</h2>
<p>Удостоверившись в данном поведении Я расстроился и написал статью до текущего параграфа. Но, желание отделить клиент и сервер не уменьшилось. И Я решил поискать архитектурные решения среди современных веб сервисов.</p>
<p>Зашел в linkedIn - ужаснулся какая кака в коде. Решения не нашел.
Зашел в Yammer. И нашел! Оказалось, что Яммер использует cometd based сервис, который находится на домене rt-123.rt.yammer.com. И использует CORS и Cookie. И… невероятно… такая связка работает в браузере с включенной блокировкой third-party-cookies.</p>
<h2 id="section-1">Эксперимент</h2>
<p>Я не понял, почему работает у них, а у меня нет. Но, Я был намерен это узнать =)
Догадки, которые были на тот момент:</p>
<ul>
<li>SSL - Возможно, хитрые браузеры не считают за third-party домены с идентичным wildcart SSL сертификатом?</li>
<li>Я опять продолбал важный параграф в спецификации CORS</li>
<li>Хаки со стороны Yammer</li>
</ul>
<p>Больше всего надежды было на SSL. А зря. Как оказалось, wildcard сертификаты распространяются только на поддомены первого уровня. А тут rt-123.rt.yammer.ru - второго. Не работает. И правда, проверив сертификат rt домена, Я обнаружил, что он другой.</p>
<p>Хаков со стороны Yammer-a обнаружено не было. Спецификацию Я перечитал.
И всеже оно заработало.</p>
<h2 id="third-party">Не third-party</h2>
<p>Оказалось, что браузер блокирует запросы к поддоменам по причине cross-origin, однако не считает их third-party. Ох, сложно. Сейчас объясню.</p>
<p>Возьмем два домена</p>
<p><code>
yourdomain.com
api.tapcat.net
</code></p>
<p>и <code>tapcat.net</code> - основной домен, с которого будем делать запросы.
При использовании CORS мы явно можем разрешить доступ к выбранным доменам. При разрешенных third-party cookies работа и с одним и с другим доменом не доставит хлопот.
А вот, если TPC заблокированы….
В этом случае, оказалось, что браузер срежет куки для домена <code>yourdomain.com</code>, но позволит проставить cookie для домена <code>api.tapcat.net</code>.
Глубина поддоменов не учитывается и все работает даже для: <code>dev.api.tapcat.net; a.b.c.d.tapcat.net</code>.</p>
<p><img src="/images/cors-cookie-chrome.png" alt="cors-third-party-chrome" />
<img src="/images/cors-cookie-firefox.png" alt="cors-third-party-firefox" /></p>
<p>Получается, оно работает потому что текущий Origin содержится в данном домене!?</p>
<h1 id="i-need-proof">I need proof</h1>
<p>Решение работает в современных браузерах. Данное поведение должно быть стандартизировано!
Ищем стандарт!
Нам подходят документы:</p>
<ul>
<li><a href="https://www.rfc-editor.org/rfc/rfc6265.txt">HTTP State Management Mechanism</a></li>
<li><a href="http://www.w3.org/TR/cors/">W3C CORS</a>.</li>
</ul>
<p>Первый говорит, что есть такие third-party cookies, которые user agent может блокировать. А как отличить одни Cookie от других - не сказано. Однако, есть интересный параграф:</p>
<pre><code class="language-html"> The user agent will reject cookies unless the Domain attribute
specifies a scope for the cookie that would include the origin
server. For example, the user agent will accept a cookie with a
Domain attribute of "example.com" or of "foo.example.com" from
foo.example.com, but the user agent will not accept a cookie with a
Domain attribute of "bar.example.com" or of "baz.foo.example.com".
</code></pre>
<p>Но, вроде, не про то. У нас то сам api.tapcat.net хочет проставить Сookie для api.tapcat.net.</p>
<p>Второй документ находится в состоянии Candidate Recommendation и ничего про нашу ситуацию не говорит.</p>
<p>А про механизм определения third-party-cookies - вообще засада. Нигде нет его описания. Помогите мне его найти!
4 дня искал и не нашел. Даже смотрел исходный код Firefox. Но, ссылок на стандарт там нет. Поиски продолжаются.</p>
<p>И, да. В результате удалось отделить статику от сервера с бизнес логикой на уровне доменов. Как Я и хотел. ;)</p>
<p><a href="/blog/cors-and-thirdparty-cookies/">CORS and Third-Party Cookies</a> was originally published by Denis Golovachev at <a href="">Post-Engineering</a> on October 17, 2013.</p>/blog/docker-openvpn2013-10-06T00:00:00+00:002013-10-06T00:00:00+00:00Denis Golovachevdenis@borov.net<p>На днях дома отключился интернет. Оказалось, что к провайдеру пришли правохраниетльные органы и забрали сетевое оборудование. Перевел весь домашний трафик через Голландию. Чтоб неповадно было. Сейчас расскажу как.
Для понижения уровня паранои нам потребуются:</p>
<ul>
<li><a href="https://www.digitalocean.com/?refcode=06c4ef09e5cc">Сервер за границей</a></li>
<li><a href="http://www.docker.io/">Docker</a></li>
</ul>
<h2 id="section">Сервер</h2>
<p>Нужна машина с 256мб ОЗУ. Ничего сверхестественного.
Порекомендовать могу <a href="https://www.digitalocean.com/?refcode=06c4ef09e5cc">DigitalOcean</a>.
5$ в месяц. Все сервера на SSD. Быстрый хелпдеск. Вот моя ссыль: <a href="https://www.digitalocean.com/?refcode=06c4ef09e5cc">DigitalOcean</a>.
А еще и докер ставится в один клик. Оооочень просто.
В один клик
<img src="/images/docker/digital-ocean-docker.png" alt="Docker VM provisioning" /></p>
<h2 id="docker">Docker</h2>
<p>Docker - обертка над lxc контейнерами. Если просто - то это инструмент, позволяющий быстро создавать и управлять сверхлегкими виртуальными машинами.</p>
<p>Зачем именно Docker? Можно было развернуть OpenVpn сервер прямо на виртуальной машине.
Ответ прост. В результате удалось получить контейнер с установленным OpenVPN, который Я могу переносить с машины на машину и запускать одной командой. Никакой донастройки!</p>
<p>Докер мне попался на глаза, как только Я решил сделать Continues Delivery для нового проекта.
CD сделал. Скоро напишу об этом.</p>
<h2 id="code">Code</h2>
<p>Начинаем. Для базы выберем образ убунты</p>
<pre><code class="language-bash">docker run -privileged -i -t ubuntu:precise /bin/bash # i - интерактивный режим. Попадаем в консоль
apt-get install -y iptables wget # iptables - для openVpn; wget - чтобы скачать openVPN AS
# Ставим OpenVPN
wget http://swupdate.openvpn.org/as/openvpn-as-1.8.5-Ubuntu12.amd_64.deb
dpkg -i openvpn-as-1.8.5-Ubuntu12.amd_64.deb
# tun утсройства в контейнере нет, но ведь оно прокинуто снаружи. Создадим его руками
mkdir -p /dev/net
mknod /dev/net/tun c 10 200
# ставим пароль админа и запускаем сервис
passwd openvpn
/etc/init.d/openvpnas start
# создаем пользователей. OpenVPN их подцепит
useradd borov
passwd borov
exit
</code></pre>
<p>Готово. Осталось сохранить контейнер.</p>
<pre><code class="language-bash"># Самый верхний ID - это наш
docker ps -a
docker commit $ID -t borov/openvpn
</code></pre>
<p>Образ готов.
Запускаем</p>
<pre><code class="language-bash"> docker run -d -privileged -p 1194:1194/udp -p 443:443/tcp -p 943:943/tcp -t borov/openvpn-server /bin/bash -c "service openvpnas start && tail -f /var/log/openvpnas.log"
</code></pre>
<p>Готово! Теперь по адресу <code>https://your-vm.com/</code> откроется консоль OpenVPN через которую можно подключиться к сети или зайти с административного аккаунта.
И больше никаких настроек!</p>
<p><a href="/blog/docker-openvpn/">Docker OpenVPN</a> was originally published by Denis Golovachev at <a href="">Post-Engineering</a> on October 06, 2013.</p>/blog/reactive-programming2013-10-03T00:00:00+00:002013-10-03T00:00:00+00:00Denis Golovachevdenis@borov.net<p>Ох, приехали. Даже в вакансии DB сегодня Я обнаружил “Experience in UI architecture (MVP, reactive programming)”
Scala в содружестве с TypeSafe всетаки продвинули еще одно слово паразит в массы.
Теперь все, до чего дотрагиватется TypeSafe начинает называться Reactive. Даже курс на курсере решили назвать Reactive Programming.
Cloud, Big Data….. теперь еще и Reactive. Ну ребята. Ну прекратите.
Особенно поразило то, что слова паразиты влезли в описание вакансий.
А ведь это всего лишь название одного из паттернов</p>
<h2 id="reactor">Reactor</h2>
<p>Паттерн. Используется для построения асинхронных приложений. Больше прочитать можно в <a href="http://en.wikipedia.org/wiki/Reactor_pattern">ВикиПедии</a>.
Примерами могут являться NodeJS и Twisted.
Также есть интересная статья, датированная 2010 готом от самого Мартина-Мартина, показывает нам новый подход к написанию интерактивных приложений. Подход - именно Reactor. Вот статья: <a href="http://lampwww.epfl.ch/~imaier/pub/DeprecatingObserversTR2010.pdf">Deprecating Observer</a>.
Подход не новый. И особо ничего уникального для языков с поддержкой Continuation нет. Но, прочитать стоит.
На этом хорошие новости заканчиваются. До текущего времени в Scala идея не получила развития (<a href="http://www.scala-lang.org/old/node/10865.html">scala.react</a>).
Даже репозиторий с черновой реализацией на Гитхабе заброшен.
Теперь о TypeSafe. В основном продукте компании используется подход с использованием паттерна Reactor. Наравне с другими паттернами.
Но, кто же решил, что Reactor - самый смак?</p>
<h2 id="section">Маркетологи</h2>
<p>Люди, подарившие нам Cloud, Big Data, Rich Client не сидят на месте. Вот оно, наше будущее: Reactive. Если в вашей компании вы не Reactive, то вы не в тренде. Блог TypeSafe постоянно на это намекает.
Новый релиз Play Framework носит сияющий шильдик ‘Reactive’. И это не мажорный релиз.
Spring Reactor - фреймворк для написания асинхронных приложений на JVM. Кто повлиял на название?</p>
<h2 id="section-1">Вывод</h2>
<p>Я очень бешусь от того, что вокруг меня начали употреблять слово Reactive направо и налево.
Возможно, это надуманно. Помогите разобраться.</p>
<p><a href="/blog/reactive-programming/">Reactive</a> was originally published by Denis Golovachev at <a href="">Post-Engineering</a> on October 03, 2013.</p>/blog/firefox-phone2013-10-02T00:00:00+00:002013-10-02T00:00:00+00:00Denis Golovachevdenis@borov.net<p>Сегодня с утра Я открыл почту и удивился. Mozilla пришлет мне телефон.
Похоже, что следующее приложение для Firefox OS не за горами ;)</p>
<p><a href="/blog/firefox-phone/">Firefox OS phone</a> was originally published by Denis Golovachev at <a href="">Post-Engineering</a> on October 02, 2013.</p>