Страницы

Поиск по вопросам

Показаны сообщения с ярлыком архитектура. Показать все сообщения
Показаны сообщения с ярлыком архитектура. Показать все сообщения

понедельник, 30 марта 2020 г.

Материалы по Clean Architecture в Android [закрыт]

#android #архитектура #mvp #dagger #moxy


        
             
                
                    
                        
                            Закрыт. Данный вопрос необходимо конкретизировать. Ответы
на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы он был сосредоточен только на одной проблеме, отредактировав его.
                        
                        Закрыт 2 года назад.
                                                                                
           
                
        
Имею 3 года практики в Android разработке и недавно начал интересоваться такими вещами,
как DI, Moxy, Cicerone и Clean Architecture. Здесь, на SO, я почти не вижу вопросов,
относящихся к этой теме. В телеграмме очень мало людей состоят в чатах по ним. Как
я понял, эти вещи изучаются в самом конце, на пути к "званию" Senior Developer'а и
сложнее них нет ничего. Год назад я не понимал суть Dagger'а и MVP. Сейчас я, кажется,
понимаю зачем это все нужно: так как разработка почти всегда командная, у всех участников
проекта должны быть определенные стандарты проектирования. Разработчики будут лучше
понимать друг друга. 

Все же полностью понять эти вещи я не могу, я думаю здесь есть те, кто тоже их не
понимал, но сейчас вполне их использует и счастлив. Хочу у них спросить какие книги
они читали и как трудно там, в клине? Из мной прочитанных и полностью изученных, являются
"Философия Java" и "Java 8 Полное руководство". Если Clean Architecture относится к
архитектуре кода, то мне нужно сначала прочитать, например, "Паттерны проектирования"
и "Эффективное программирование"? Какие книги я должен для начала прочитать?

Также хотел бы узнать какая сейчас средне ситуация с ними на рынке, они уже must
have или пока еще в будет плюсом?

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

Спасибо. Думаю, что книги, которые вы посоветуете (и Clean Architecture в общем)
будут интересны многим.
    


Ответы

Ответ 1



Роберт Мартин, человек описавший чистую архитектуру, написал книгу с соответствующим названием - "Чистая архитектура". Его фундаментальный труд в виде статьи лежит здесь. Разъяснения касательно разработки под android, и clean architecture в целом, расписаны в этом труде. Материалы по второй и третьей ссылке дадут вам надежную почву для старта.

пятница, 13 марта 2020 г.

Как избавиться от параллельных иерархий наследования?

#java #ооп #архитектура


Я решаю задачу о нахождении лидера (leader election)
Это чисто алгоритмическая задача, у которой есть 2 формы: однонаправленное кольцо
и двунаправленное. Для представления данных я создал свой собственный класс для списка,
закрученного в кольцо. То есть у меня два алгоритма, по одному для каждой формы задачи.

public abstract class MyAbstractRoundList {

    protected int size;
    protected Agent[] arr = new Agent[1];
    protected int index = 0;

    public boolean isEmpty() {
        return size == 0;
    }

    public int size() {
        return size;
    }

    public void add(Agent agent) {
        if (size >= arr.length) {
            Agent[] temp = arr;
            arr = new Agent[temp.length * 2];
            System.arraycopy(temp, 0, arr, 0, temp.length);
        }
        arr[size++] = agent;
    }
// и дальше еще много методов


Далее я создаю наследников этого класса для однонаправленного режима и для двунаправленного.
Реализации в них конечно различаются. Далее, у меня есть класс:

public class LeaderElection {

public static void solve(MyAbstractList list, int i) {
    if (i == 0) {
        list.initiateStartState();
    } else {
        list.setMessages();
    }
}


}

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

    if(isOneDirectMode()){
        LeaderElection.solve();
    {
    if(isBiDirectMode()){
        BiLeaderElection.solve();
    }


Я мог бы так же создать абстрактный класс для решения и его наследников и пользоваться
полиморфизмом (так я и делаю для списков). Но вот тут и возникает проблема: у меня
получаются параллельные иерархии наследования. Если появится новый режим я должен буду
добавить новый класс подкласс MyAbstractRoundList и новый подкласс решения. Можно как
то избавиться от этого? Я вижу только один способ: Можно было бы сделать в MyAbstractRoundList
абстрактный метод solve() и реализовывать его в подклассах каждого режима так как это
требуется для конкретного режима. Но это плохо, потому что тогда у меня подклассы MyAbstractRoundList
будут не только хранить данные, но и решать задачу. То есть выполнять 2 функции.
    


Ответы

Ответ 1



Они не параллельны. Изменения в одной иерархии никак не влекут за собой изменения в другой иерархии. Если появится новый режим я должен буду добавить новый класс подкласс MyAbstractRoundList и новый подкласс решения. Можно как то избавиться от этого? Я вижу только один способ: Можно было бы сделать в MyAbstractRoundList абстрактный метод solve() и реализовывать его в подклассах каждого режима так как это требуется для конкретного режима. Решение задачи - это поведение. Поведение лучше описывать интерфейсами. public interface Solver { void solve(MyAbstractList list, int i); } ... public class LeaderElection implements Solver { ... public class BiLeaderElection implements Solver { ... public class MultiTreadingLeaderElection implements Solver { У вас отдельная иерархия наследования классов, отвечающих за хранение данных, и отдельная иерархия классов, отвечающих за решения. Это нормально. Если появится новый вид хранения (новый тип списка), вы добавите новый класс-наследник MyAbstractRoundList. Это никак не связано с решениями, новый класс может использоваться и существующими решениями. Если потребуется добавить новый способ решения, вы добавите новый класс, реализующий интерфейс Solver, и это не обязательно должно отразиться на иерархии классов для хранения. При таком подходе у вас всегда будет работать код solver.solve(abstractList, i);, если в переменную solver положить объект любого класса, реализующего интерфейс Solver, а в переменную abstractList - любой объект класса-наследника MyAbstractRoundList, и самое главное - этот код не придется менять при добавлении новых классов, как и должно быть с точки зрения ООП. UPD. Есть данные, есть поведение. В рамках класса поля описывают данные, методы - поведение. Например, я руковожу бригадой роботов. Одни из них могут строить, другие - переносить, третьи - разрушать, четвертые - поливать. Робот-строитель, например, может принести себе стройматериал, но только в небольшом количестве, таким образом, он может и строить, и носить. А робот-носитель - только носить. И если вдруг поступает задача срочно разгрузить вагон стройматериалов, то мне надо собрать всех роботов, умеющих носить. И мне плевать, что это за роботы. Хоть робот-поливалка. Если может носить - пусть идет носить. Я просто выберу роботов с нужным мне поведением, и буду уверен, что они смогут сделать то, что мне надо. public interface Carrier { void carry(); //нести } public interface Builder { void build(); //построить } public interface Destroer { void destroy(); //сломать } public class RobotBuilder extends Robot implements Builder, Carrier { public void carry() { } public void build() { } } public class RobotDestroer extends Robot implements Destroer { public void destroy() { } } public class RobotCarrier extends Robot implements Carrier { public void carry() { } }

четверг, 5 марта 2020 г.

Как правильно реализовать морфинг Hamburger'а в back button?

#android #архитектура


Я хочу использовать подход: "каждому фрагменту свой Toolbar".
Как в этом случае правильно реализовывать анимацию морфинга
Hamburger -> BackButton
при переходах между фрагментами?
    


Ответы

Ответ 1



У вас получается такая ситуация: Toolbar принадлежит фрагменту, а иконка внутри Toolbar отображает состояние стека фрагментов внутри активити. Если вам действительно нужна эта анимация перехода от гамбургера к стрелке, то есть два варианта: 1) Делаете глобальный Toolbar в активити. Но тогда будут все вытекающие кейсы с инфлейтом разных меню и заголовков из конкретных фрагментов. 2) Делаете в активити метод, который синхронизирует переданный Toolbar с состоянием стека. Но надо не забывать отсоединять Toolbar, когда фрагмент уходит с экрана (логично использовать методы старт/стоп) fun attachToolbarNavigationIcon(toolbar: Toolbar) {} fun dettachToolbarNavigationIcon() {} ЗЫ: Есть более простой вариант. Скорее всего анимация из "гамбургера" в кнопку "назад" вам нужна только при переходе с первого фрагмента на следующий. В остальных случаях там висит только кнопка "назад". Поэтому можно это обработать только в рамках первого фрагмента и все: перед переходом вперед - превращаем в кнопку назад и только тогда переходим. При возврате превращаем обратно в гамбургер.

Ответ 2



Не могу дать Вам готового решения, но в общем случае в support library есть готовый класс DrawerArrowDrawable, который как раз реализует эту морфирущую иконку. Вы всегда можете попробовать добавить её на Ваш Toolbar и запустить анимацию после того, как тулбар нового фрагмента добавится на экран. Пример запуска анимации вот тут. Если Вы используете Navigation Drawer, то иконка должна работать "из коробки". Вот здесь есть схожий вопрос с ответами, как при этом использовать разные тулбары (обратите внимание не только на принятый ответ, но и на ответ с большинством голосов).

среда, 26 февраля 2020 г.

В чем разница между presenter'ом и interactor'ом в чистой архитектуре?

#архитектура


Разбираюсь с чистой архитектурой. Не доходит один момент: на сколько я понимаю цепочка
работает примерно так:


  controller <-> persenter <-> interactor <-> repository


Если рассмотреть это на примере метода, то получается, что контроллер вызывает метод
presenter->getBooks(), далее презентер вызывает интерактор: interactor->getBooks()
и только потом происходит обращение к репозиторию. 

При этом, presenter и interactor просто вызывают один и тот же метод. Так в чем же
их разница?

Скорее всего подразумевается, что в них должна происходить некая обработка данных
перед возвратом результата функции, но какая логика и где должна выполняться?
    


Ответы

Ответ 1



Clean Architecture подразумевает, что код разделен на 4 уровня со следующим правилом зависимости: внутренний уровень не должен зависеть от каких-либо внешних уровней. Это означает, что зависимости должны указываться внутри каждого уровня, чтобы не было зависимостей между уровнями (слоями). Соответственно сущности Presenter и Interactor(Use Cases) лежат на разных уровнях и несут разную смысловую и функциональную нагрузку. Presenter обрабатывает события от пользовательского интерфейса (UI) и работают как callbaсk-и из внутренних уровней (Interactors). Presenters являются легко тестируемыми объектами и их основная задача - получить данные от приложения и преобразовать их так, чтобы Представление (View) могло просто переместить их на экран. Interactor же фактически содержит бизнес-логику приложения (проверка каких либо условий и обработка данных). Они работают в фоновом режиме и передают события и данные верхнему уровню (Presenters) c помощью callbaсk-ов. Это полностью соответствует принципу единственной ответственности, который гласит, что каждый объект должен иметь одну ответственность и эта ответственность должна быть полностью инкапсулирована в класс. Все его поведения должны быть направлены исключительно на обеспечение этой ответственности. Например над проектом работает несколько команд, каждая из которых разрабатывает код на определенных слоях. Когда разработчики компонента Presenter-а поменяют что-то в коде и захотят протестировать его, им просто нужно собрать свою версию Presenter-а с версиями компонентов Interactors и Entities, используемыми в данный момент. Никакой другой компонент в системе им не потребуется для этого. Это означает, что разработчикам Presenters не придется прилагать значительных усилий для подготовки к тестированию и им достаточно учесть небольшое количество переменных. Вышеописанный пример легко проследить на диаграмме компонентов, представленной ниже. Очень важно заметить, что в такой диаграмме компонентов есть особенность: с какого бы компонента вы ни начали, вы не сможете пройти по связям-зависимостям и вернуться обратно в этот же компонент. Эта структура не имеет циклов - то есть это ациклический ориентированный граф.

понедельник, 24 февраля 2020 г.

Ревью Архитектуры приложения

#c_sharp #архитектура #шаблоны_проектирования #инспекция_кода


Есть решение разбитое на следующие слои: DAO, DAL, Services Gui где,


DAO - здесь хранятся классы описывающие доменнные модели;
DAL - Generic Repository и его реализация; 
Services - здесь у меня методы по типу следующего: IEnumerable GetEntities();
Gui - клиентская часть


предвижу сразу вопрос чем Services отличается от Repository, в репозитории у меня
базовые методы CRUD:

void Create(Entity entity);
void Update(Entity entity);
void Delete(Entity entity);
IQueryable Table {get;}


в Services же у меня строются более сложные запросы: соединения, группировка например:

var _orders = получаем данные;
var _histories = получаем данные;
var orders = _orders.Join(_histories, o=>o.Id, h=>h.OrderId,(o,h)=>new OrderView
                    {
                        //Формируем необходимое представление
                    })
                    .ToList();


ну и т.п.

зависимости между проектами следующие:


DAO подключен в качестве reference в DAL, Services, Gui
DAL подключен в качестве reference в Services, Gui
Services подключен в качестве reference в Gui


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

Правильно ли я поступил разбив проект на более мелкие части, или я сделал так зря
и необходимо слить все в один проект а разделение сделать на уровне директорий внутри
решения и namespace.

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


Ответы

Ответ 1



Есть самые разные способы проектирования приложения. Как я вижу вы пытаетесь использовать DDD. Этот паттрен достаточно универсален, но не является сильвербуллетом, а так же имеет очень много вариаций реализации, например CQRS+ES или DDD+Onion которые имеют свои назначения, а вместе с ним свои плюсы и минусы. По мимо DDD и его вариаций, существуют такие архитектурные паттерны как EDA, SOA, NLayer и другие. Что бы поддержка не причиняла больших проблем, нужно сначала выбрать правильную архитектуру, а это вопрос не только правильного выбора паттерна, но языка, стека технологий и многого другого. Сам же выбор зависит от решаемой задачи и различных условий.

Ответ 2



Был у меня опыт разработки именно при такой архитектуре, только за место Entity Framework использовался NHibernate. Ну и был еще один дополнительный слой абстракции для веб сервисов. То бишь было параллельно несколько веб сервисов, которые пользовались фунциями из слоя Services. В целом код получался весьма читабельным и понятным. Но вместе с этим при реализации функции в GUI приходилось протягивать эту функцию через все слои это со временем начинало бесить. Получалось как-то слишком много лишнего кода который приходилось писать лишь из-за всех этих слоёв. Дебаг так же происходил чуть медленнее. Были некоторые трудности с определением того куда стоит засунуть функцию, в DAL или Services. В целом считаю данную архитектуру весьма адекватной для решения многих задач. Она предлагает высокую степень гибкости и читабельности, но может стать очень громоздкой. Что бы избавиться от громоздкозти этого монолита мне кажется, можно было бы начать отщипывать от монолита микросервисы.

вторник, 18 февраля 2020 г.

Передача id сущности vs Bundle: плюсы и минусы?

#android #архитектура


Есть список сущностей, например Event. По клику на сущность переходим на детальный
экран. Можно ли передавать сущность в Bundle или лучше передавать только Id. Какие
плюсы и минусы у этих подходов?
    


Ответы

Ответ 1



Правильнее передавать параметром только Id, а сами элементы будут получаться из репозитория по этому айди. Если у вас есть что положить в Bundle, значит вы можете закешировать это через репозиторий. В таком случае вы всегда можете получить актуальную информацию из репозитория, а не потенциально устаревшую из параметров. К тому же, если вам нужно передать объект с большим количество полей, или со списками данных, в определённый момент у вас может произойти TransactionTooLargeException, если вы будете использовать Bundle.

воскресенье, 16 февраля 2020 г.

Передача id сущности vs Bundle: плюсы и минусы?

#android #архитектура


Есть список сущностей, например Event. По клику на сущность переходим на детальный
экран. Можно ли передавать сущность в Bundle или лучше передавать только Id. Какие
плюсы и минусы у этих подходов?
    


Ответы

Ответ 1



Правильнее передавать параметром только Id, а сами элементы будут получаться из репозитория по этому айди. Если у вас есть что положить в Bundle, значит вы можете закешировать это через репозиторий. В таком случае вы всегда можете получить актуальную информацию из репозитория, а не потенциально устаревшую из параметров. К тому же, если вам нужно передать объект с большим количество полей, или со списками данных, в определённый момент у вас может произойти TransactionTooLargeException, если вы будете использовать Bundle.

четверг, 13 февраля 2020 г.

Поясните за проектирование и оформление кода [закрыт]

#php #архитектура #проектирование #code_style


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

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

Раньше я делал такие конвееры многократными структурами if/elseif/else, switch/case,
goto, чтобы выполнение кода циклически перебрасывалось из одного блока в другой. Столкнулся
с большой сложностью - читабельность и понимаемость кода из-за многократной вложенности
конструкций, сложность отладки, избыточное количество переменных, в которых запутываешься.
В итоге кодинг превращается в настоящую головоломку, и каждый новый проект вызывает
FFFFUUUUU-реакцию, когда приходится это снова повторять.

Сейчас занялся очередным таким проектом, и понимаю, что я неправильно делаю, и можно
проще, независимо от уровня сложности, просто я не знаю как. Подскажите, что я не так
делаю, и как делать правильно?
    


Ответы

Ответ 1



Мини-ремарка: Лол, что не так с вопросом? Хоть бы уточнили Вы не поверите, но описанные в вопросе проблемы, с которыми вы столкнулись, и есть та огромная разница между хорошим опытным программистом и новичком. Каким вы себе представляете ответ на такой "не маленький" вопрос? :) Теперь попытка краткого ответа: Правильно ставьте задачу. Это действительно половина ответа. Производите декомпозицию, т.е. разбиение задачи на небольшие подзадачи. Меньше задача - меньше переменных в голове, яснее цель, проще и короче решение, меньше число вариантов решения, проще сосредоточиться и/или переключить контекст в голове, если что-то вас отвлекло. Думайте не в терминах языка программирования (if+else, do+while и т.д.), а в терминах той области, в которой вы решаете задачу. Не в терминах типов хранения/представления данных (int, string и т.д.), а в терминах объектов, фигурирующих в текущей области знания. Не вы "перемещаете" и "изменяете" данные, а объекты как бы сами живут и меняются, исполняя некоторые обязательства, взаимодействуют друг с другом. Отнеситесь серьезно к форматированию и организации кода. Более того, правильно примененная декомпозиция задачи не оставит вам шансов писать процедуры/функции/методы, которые не умещаются по высоте на одной странице монитора. Но позаботьтесь так же об адекватном именовании абсолютно всего. Код должен быть таким, чтобы практически любые комментарии были излишни (кроме достаточно глобальных, являющихся документацией к классу/модулю, или очень специфических). Найдите середину между одно-двухбуквенными названиями переменных и оченьДлиннымиНазваниямиКоторыеЧитатьУжеНуСовсемНеудобно. Помните, что многопоточку дебажить в принципе сложно. Поэтому очень важно максимально устранить в коде остальной "дискомфорт". Upd. Шаблонов и концепций придумано много - по формулировке задачи не очевидно, что лучше подойдет (да и я не эксперт, если честно). Я бы старался максимально избегать многопоточки (не асинхронности) - очень часто профит от ускорения не перекрывает затрат на сложность разработки и поддержки. В простейшем случае, вы разбиваете вашу задачу на независимые этапы, для каждого из которых реализуется свой обработчик. Позаботьтесь о том, чтобы обращение к общим данным было потокобезопасным и его было как можно меньше - чем более независимым будет обработчик каждого этапа, тем меньшей головной болью будет одновременный запуск нескольких обработчиков. Обозначьте зависимости и требуемые входные данные для каждого типа обработчиков, а также данные на выходе. После этого достаточно лишь организовать механизм сохранения состояния и общения между обработчиками соседних этапов (т.е. переход от этапа к следующему) - это могут быть очереди, шины, сокеты, обычные колбэки событий и т.п. Подумайте над тем, кто будет инициировать переход между состояниями (push-based, pull-based, centralized). На русском гуглить наверное будет очень сложно (у меня сходу довольно скудные результаты получились). Теги: multithreading pattern, asynchronous pattern, event-driven programming, task-based asynchronous pattern, немного про разницу между асинхронностью и многопоточностью.

Где хранить текущие данные программы?

#java #ооп #android #архитектура


Мое приложение имеет некие динамические пути, которые формируются при каждом запуске.
И, к которым мне нужно иметь доступ из любой части программы. Сейчас я пользуюсь объектом
static. Меня все устраивает. Однако в статье на Хабре не рекомендуется хранить данные
в static, связано с жизненным циклом Activity.

Остается либо property файл, либо SQLite, но меня смущает "тяжесть" этих способов.
Быть может есть еще какие варианты? Или все-таки стоит остановиться на SQLite?
    


Ответы

Ответ 1



Всё зависит от объёма и сложности ваших данных. Если вам достаточно хранить строки/массивы строк/числа, то можно пользоваться SharedPreferences: //сохраняем строку в файл внутренней директории приложения SharePreferences pref=PreferenceManager.getDefaultSharedPreferences(context); pref.edit().putString("key", "value").commit(); //получаем ранее сохранённые данные String savedData=pref.getString("key"); Если что-то сложнее, то да - используйте БД.

Ответ 2



Для этого придумали паттерн Singleton. @EBean(scope = Scope.Singleton) public class MySingltoneBean { //Тут прописываем геттеры сеттеры и прочие методы доступа к общим переменным } Теперь, когда нам надо воспользоваться нашими общими данными из Activity достаточно в нем определить @Bean MySingltoneBean mySingltoneBean;

Ответ 3



Я использую немного иной способ (самый легкий): создаю Java класс (не Activity, а просто класс) и в нем храню статические переменные. Так они доступным всем и отовсюду, а так же не зависят от жизненного цикла Activity. Главное в этом способе — простота. Пример: class Resources { public static int myNumber = 2334595; } Теперь переменную myNumber может получить и изменить любая активность (и фрагменты, и все остальные). Еще хорош способ, который описали выше. Но тот способ - для хранения данных, которые должны сохранится после закрытия приложения, а мой — во время работы приложения (Вы же сказали, что они создаются при запуске).

суббота, 8 февраля 2020 г.

Научиться проектировать архитектуру приложения [закрыт]

#c_sharp #книги #архитектура #проектирование


        
             
                
                    
                        
                            Закрыт. На этот вопрос невозможно дать объективный ответ.
Ответы на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы на него можно было дать ответ, основанный на фактах и цитатах, отредактировав его.
                        
                        Закрыт 4 года назад.
                                                                                
           
                
        
Стал замечать, что по мере освоения C#, я все больше думаю о том, как архитектурно
реализовать ту или иную часть приложения.
Например, разрабатывая приложение для взаимодействия с USB-устройством, задумался
над тем, а как именно будут взаимодействовать его компоненты. 
Например, можно реализовать единственный класс, который будет заниматься всем: работать
на уровне WinAPI с USB, ожидать прихода пакетов, понимать протокол общения, реализовывать
последовательности команд и обработку ответов на них и т.д.
Но это неудобное решение. Лучше разделить по слоям и, например, вынести общение по
USB в отдельный модуль и тогда, если USB поменяется на Ethernet, то можно будет просто
написать новый модуль, не трогая остальное. 
Окей, пишем модуль общения по USB. Для этого пишем сначала интерфейс взаимодействия
с верхним уровнем. Написали. Так, что-то тут все заточено под синхронный обмен, а вдруг
понадобится асинхронный. Дополняем. И еще несколько параметров. 
Начинаем писать сам модуль. Оказывается, заложенная функциональность избыточна. А
еще вот тут что-то не так и там и т.д. Опять начинаем думать, исправлять/дополнять.
В итоге я зарываюсь в вопросы из серии "а как лучше сделать сейчас, чтобы в будущем
было удобно поддерживать?", производительность падает, а сложность придуманных конструкций
резко растет и в итоге получается какая-то каша, а не идеальный код, который я и хотел
получить.
Что я делаю не так? Как поступают опытные программисты? Как они избегают этих бесконечных
вопросов и при этом пишут вполне поддерживаемый и понятный код? 
Какую литературу можно почитать, чтобы прокачаться в этих вопросах? Шаблоны проектирования?
А не слишком ли они заточены под крупные проекты и сложны для небольших проектов? 
И есть ли у кого ссылки на проекты на C#, где можно посмотреть архитектурные решения?     


Ответы

Ответ 1



@yabloko, умение проектировать и находить баланс между текущими потребностями проекта и созданием задела на будущее приходит только с опытом. Нельзя просто прочитать книжку и обрести просветление. Поэтому пишите код, обдумывайте, изучайте как устроены чужие проекты, моделируйте какие-нибудь примеры взятые с потолка. Проектируйте для разминки небольшие "взятые с потолка" фрагменты функциональности. Просто на бумажке. Например: простенький UI из текстовых полей и кнопок, но такой, чтобы при необходимости можно было расширить количество виджетов; список контактов, с возможностью импорта из разных источников (почта, twitter, соцсети); текстовый редактор с простыми операциями над текстом (например, сделать выделенный фрагмент КАПСОМ) и возможностью их отмены. и т. п. В общем фантазируйте и тренируйтесь. Кстати про проектирование полноценного текстового редактора подробно разжевано в "Банде четырех".

Ответ 2



Попробуйте думать в терминах сущностей и их ответственности. Какие сущности есть в вашей программе, кто за что отвечает, кто о чём знает? Если вы не можете описать одним коротким предложением ответственность модуля/класса/функции/переменной, возможно, её надо переделать. Вы не должны думать о том, нужно ли будет подменить кусок с USB на кусок с Ethernet. Это может оказаться преждевременным дизайнерским решением, overengineering. Но вот разделение на отдельные простые слои — правильная мысль, она поможет вам бороться со сложностью проекта, сосредотачиваться каждый раз только на нужной части. Точно так же, вы выделяете код в процедуру, если он имеет самостоятельный смысл, а не для повторного использования. Даже если процедура используется всего один раз. Стоит почитать какую-нибудь книгу по паттернам, но не увлекайтесь этим сильно: свои мозги всяко лучше, чем консервированные идеи. Тем более, то, что в одном языке — паттерн, в другом часть синтаксиса (пример: Observer в C# реализуется в одно объявление, а в C++ требует уйму boilerplate-кода).

Ответ 3



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

среда, 5 февраля 2020 г.

В каком слое нужно реализовывать логику выбора источника данных (локальные данные или сеть)?

#android #архитектура


В каких случаях Domain слой (бизнес-логика) может знать о существовании разных источников
данных? Например, у меня есть такой код:

GetUserUseCase {

    UserRepository userRepository;

    GetUserUseCase(UserRepository userRepository) { 
       //... 
    }

    public Observble getUser(int id) {
        return Observable.merge(userRepository.getLocal(id), userRepository.getRemote(id));
    }

}


т.е. мой репозиторий может отдавать данные из двух источников. Насколько это корректно
с точки зрения "Чистой архитектуры"?

Это простой пример, у меня есть более сложные кейсы, когда мне необходимо точно знать
из какого источника получены эти данные. Должен ли я пересмотреть свою архитектуру
таким образом, чтобы UseCase не знал о разных источниках, т.е. у меня в репозитории
был бы один метод getUser(), или это нормальная ситуация?
    


Ответы

Ответ 1



Зависит от требований к приложению, глобально есть два подхода: 1) Умный репозиторий Тогда это на уровне репозитория, а интерактор только просит данные. То есть бизнес логика без понятия откуда и как получены данные. А репозиторий управляет всеми кешами. 2) Глупый репозиторий Репозиторий только знает как достать и как сохранить данные, а интерактор сам говорит откуда и когда. А также именно интерактор решает когда сохранять, очищать данные и так далее Обычно на практике более прост первый подход, так как пользователю и логике приложения не известно про локальные кеши и прочее.

воскресенье, 2 февраля 2020 г.

Межпроцессное взаимодействие, nodejs + nodejs, nodejs + qt и не только

#qt #nodejs #архитектура


Есть необходимость реализовать двухсторонний обмен данными между разными частями системы.
На данный момент в системе присутствуют:


модуль взаимодействия с внешними пользователями через rest api, реализован на nodejs;
модуль в котором реализована бизнес логика и взаимодействие с БД, реализован на qt;


Планируется:


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


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

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


Ответы

Ответ 1



Есть масса методов взаимодействия между процессами. К примеру, его можно организовать с помощью простого TCP сокета. Для этого есть поддержка в node.js из коробоки. В Qt для этого используется QTcpSocket. Это самый простой вариант, потому что и там и там есть поддержка из коробки и на выходе мы имеем асинхронный IPC. Другим вариантом может быть использование разделяемой памяти. Судя по всему, есть горстка дополнений для node.js добавляющих поддержку оной. В Qt есть готовый класс QSharedMemory. Этот метод хуже, т.к. требует дополнительных плагинов и он синхронен(нужно «опрашивать» разделяемую память). Идём дальше: D-Bus. В node.js есть дополнение для этого, в Qt тоже есть: Qt D-Bus. Безусловно, можно ещё придумать способов. Что из вышеприведённых лучше? Я считаю, что самым простым и удобным будет использование обычного TCP сокета.

Ответ 2



Для организации взаимодействия типа запрос-ответ хорошо подходят пакетно-ориентрованные сокеты типа UDP. В отличие от поточно-ориентрованных, где данные записанные несколькими командами записи могут быть прочитаны за один раз и наоборот, для UDP одной команде записи соответствует одна команда чтения. Таким образом один запрос или ответ упаковываются в один пакет и принимаются одним вызовом read или recv, в Qt readDatagram, в node.js - в обработчике события 'message'. Так как обмен происходит внутри одной машины, за потерю пакетов можно не беспокоиться.

пятница, 31 января 2020 г.

Зачем нужен класс FilterInputStream?

#java #архитектура


Просмотрев документацию и исходный код класса FilterInputStream, а также код его
наследников, возник вопрос: зачем вообще нужен этот класс? 

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

Как я сейчас понимаю - система ввода/вывода java.io использует паттерн декоратор.
Если классам наследникам FilterInputStream необходимо делегировать часть поведения
потоку ввода, ссылку на который они принимают у себя в конструкторе, они не обращаются
к методам FilterInputStream, а вызывают эти методы сразу по этой ссылке. Единственная
вещь в FilterInputStream, которая используется всеми его наследниками, это ссылочное
поле protected volatile InputStream in. Но ведь это всего лишь одно единственное поле
и совершенно не сложно объявить его в самих наследниках FilterInputStream.

Пожалуйста, скажите прав я или нет, и если нет — то в чем я ошибаюсь или что я не учел.
    


Ответы

Ответ 1



Единственная вещь в FilterInputStream, которая используется всеми его наследниками, это ссылочное поле protected volatile InputStream in. По порядку, все что делает FilterInputStream: содержит поле in, которое наследники могут использовать; объявляет конструктор, который принимает InputStream — каждому наследнику придется объявить хотя бы один конструктор и вызвать конструктор FilterInputStream; создает реализации по-умолчанию для всех методов InputStream, делегируя их вложенному потоку — девять методов, которые наследники могут не переопределять. Т.е. если бы не было FilterInputStream каждому из его наследников пришлось бы: объявить поле для вложенного потока; принимать в конструкторе поток и инициализировать поле; создать шаблонные реализации для всех методов InputStream вида: public int read() { return in.read(); } За счет наличия шаблонных реализаций наследник может переопределить только те методы, поведение которых специфично для наследника. Например, CheckedInputStream переопределяет три метода FilterInputStream, а наследует шесть. В документации указано 11 наследников FilterInputStream (это только в стандартной библиотеке). Дублировать поле и инициализацию уже было бы плохо. Вставлять же в каждый из классов одинаковые делегирующие методы недопустимо.

Ответ 2



FilterInputStream - это декоратор для InputStream. Декораторов на InputStream существует целое множество: к примеру, BufferedInputStream и ZipOutputStream. Он реализует все те же методы, что и InputStream, который лежит в нём, но позволяет к ним добавить доп. функционал. Конкретно его используют для какой-либо модификации данных из InputStream (т.е. фильтрации). Для лучшего понимания почитайте про паттерн декоратор

вторник, 28 января 2020 г.

Способы уменьшения числа запросов к базе данных

#оптимизация #база_данных #архитектура #sql


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


Ответы

Ответ 1



Вы в принципе все способы и перечислили. Основной - кеширование. возможно ли такое соотношение одна страница = один запрос к БД? при том, что данные реально разные. Очень абстрактный вопрос. Он решается на уровне существующего приложения. Судя по вопросам, вы совершаете типичную ошибку начинающего - "оптимизация на спичках". Задумываетесь об оптимизации, еще не зная узких мест приложения.

Ответ 2



Как правило, основной принцип оптимизации - это сделать так, чтобы количество запросов не зависело от количества данных. Т.е., не было такого, что у Вас получается большая таблица, обходятся её строки, и при обработке каждой из них делаются мелкие запросы. Если без этого не обойтись, можно, например, строки обрабатывать группами, скажем, по 1000 штук. Обращаю внимание, что речь идёт именно о мелких запросах, для которых обращение к БД более ресурсоёмко, чем собственно выполнение запроса. Модули, по идее - это не данные, их в приложении фиксированное количество. Можете попробовать создать объектную модель запроса, которая будет строиться модулями по кирпичикам (если, конечно, у Вас все запросы от разных модулей для одной операции идут к одной таблице).

Ответ 3



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

Ответ 4



Вопрос очень сферический и очень в вакууме. "У меня тут есть что-то с модулями, оно запросы делает, надо оптимизировать". На универсальный вопрос могу ответить только универсальный ответом. Используй кеширование. Где как и что кешировать -- зависит от специфики приложения, про которую неизвестно ничего. Ну и еще: возможно ли такое соотношение одна страница = один запрос к БД? при том, что данные реально разные. Это называется денормализация. Такой подход предлагают многие не-реляционные (NoSQL)базы данных. Естественно это противоречит реляционной модели данных, это несёт некоторые дополнительные риски.

пятница, 24 января 2020 г.

Индикатор работы процесса

#архитектура #java #javafx #шаблоны_проектирования


Как правильно организовать индикацию состояния выполнения задачи, с минимальным взаимодействием
с интерфейсом пользователя? Например, программа выполняет долгий расчёт(возможно в
отдельном потоке), а пользователю отображается прогресс бар в GUI. Думал создать какой-нибудь
промежуточный объект, который будет передаваться задаче.А этот, назовём его ProgressMonitor
будет посылать сообщение интерфейсу о том, что процесс идёт. В общем, выполняемая задача
не должна знать о GUI. Желательно, чтобы и GUI знал поменьше о задаче, в идеале нужно
лишь выполнить функцию StartTask, а всю работу будет выполнять задача.
Может кто-нибудь знает другое решение? Есть ли паттерн проектирования на этот случай?
На всякий случай дополню, что пишу на Java. В качестве библиотеки для GUI использую
JavaFX.    


Ответы

Ответ 1



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

среда, 22 января 2020 г.

Как грамотно подойти к верстке сайта чтобы в дальнейшем было легко его масштабировать? [закрыт]

#html #css #вёрстка #архитектура


        
             
                
                    
                        
                            Закрыт. Данный вопрос необходимо конкретизировать. Ответы
на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы он был сосредоточен только на одной проблеме, отредактировав его.
                        
                        Закрыт 2 года назад.
                                                                                
           
                
        
Здравствуйте, собираюсь разрабатывать большой web проект городской, с большим количеством
функционала, но на первой стадии буду делать только верстку. 
Посоветуйте пожалуйста как распределить фалы JS css  и др., как лучше всего поделить
стили на библиотеки и т.д. Главная задача в том чтобы потом было легко все заменять
как модули.
Есть есть статьи по этому поводу напишите пожалуйста. 
    


Ответы

Ответ 1



В данной статье есть примеры разных подходов (архитектур), с их достоинствами и недостатками, по размещению css (в оригинале scss), js и других файлов в проекте. Примеры архитектур из статьи: Функциональное распределение Распределить функциональность на отдельные файлы. _mixins.scss _variables.scss Распределение «Катана» Разделить страницы на части и обозначить стили для каждой части индивидуально. /base /sections _header.scss _content.scss _footer.scss _sidebar.scss _modals.scss /menu _left.scss _right.scss _top.scss _bottom.scss Шаблонное или страничное распределение /base /templates _category.scss _footer.scss _header.scss _index.scss _page.scss _single.scss В терминах веб-дизайна Как и предполагает название, она предназначена для веб-дизайнеров. Не использовать папки. _normalize.scss _buttons.scss _footer.scss _grid.scss _header.scss _icons.scss _navigation.scss _typography.scss screen.scss И так далее... Это может вас натолкнуть на мысли по поводу своей архитектуры.

воскресенье, 12 января 2020 г.

Проектирование базы данных при DDD (Domain-Driven Design)

#база_данных #архитектура #проектирование #ddd


Постараюсь кратко описать мой теоретический и не очень актуальный, но интересующий
вопрос. 

В подходе DDD при проектировании приложения отталкиваются от проектирования доменной
логики. Если есть у кого-то опыт, посоветуйте, как тогда проектировать базу данных.
А то подход DDD мне теоретически понятен когда незыблемая БД уже есть. А если ее нет
и ведется командная разработка и когда одну часть системы делает одна команда разработчиков,
а другую - другая, то уже совсем не понятно. Если сначала проектировать БД для всей
системы, то название domain-dd не совсем себя оправдывает, так как отталкиваемся уже
не от проектирования домена, а от проектирования БД...



Постараюсь теперь подробнее описать вопрос, с приведением простого теоретического
примера конкретной ситуации, в которой этот вопрос может возникнуть.

В DDD есть два термина:


Ubiquitous language - повсеместный язык
Bounded context - ограниченный контекст


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

Например, если рассмотреть какую-нибудь систему ERP для планирования ресурсов предприятия,
в котором сотрудники сидят и с помощью приборов делают работы. В этой системе могут
быть следующие сущности: 


Сотрудники 
Приборы 
Работы 


У системы могут быть две функции: 


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


На сколько я понимаю, понятие ограниченного контекста (bounded context) введено,
чтобы разграничить проектирование приложения на части, чтобы отдать эти части для разработки
разным командам программистов. На сколько я понимаю, в данном примере ограниченные
контексты это и есть две функции системы: учет пользователей и учет приборов.

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

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

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

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

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


Ответы

Ответ 1



На сколько я понимаю, понятие ограниченного контекста (bounded context) введено, чтобы разграничить проектирование приложения на части, чтобы отдать эти части для разработки разным командам программистов. Мне кажется, что в первую очередь здесь имеет смысль разделение на модули, а возможность разработки различными командами это уже следствие такого разделения. Так как у сущности может быть множество атрибутов которые, зачастую, используются небольшими группами в различных функциях проще сразу разделить такую сущность на эти группы атрибутов и использовать их независимо, чем везде таскать одну и туже сущность большая часть которой, в заданном контексте, не нужна и образует только лишнюю связанность кода. У Вас есть сервер приложений где описана модель Ваших данных и бизнес-логика которая умеет менять состояние этой модели. Модели должно быть все равно где и как хранится ее состояние. В Вашей ситуации главное то, как выглядит модель данных на сервере приложений и то, как реализована ее бизнес-логика. Ведь описание сущностей и их атрибуты ровно как и методы этих сущносетй должны быть понятны всем участникам процесса и соответсвовать тому как они все себе это представляют опираясь на общий язык. На сколько я понимаю, DDD подразумевает длительный процесс выработки модели предметной области в течении которого она будет достаточно часто меняться пока более менее не устаканится. В таком случае сразу скооперироваться и сделать общую базу данных так просто не получится, так как модель еще толком не ясна. Поэтому, в первую очередь, Вы будете создавать и менять модель данных на сервере приложений и уже во вторую очередь придумывать как она будет хранится. Но это не означает то, что в процессе изменения модели данных так же сильно должно меняться и хранилище данных (может меняться только слой абстракции в виде представлений). Что касается БД - то тут можно пойти по разному. У вас есть база данных которая хранит состояние Вашей модели и осуществляет проверку целостности данных. На БД часто налагают различные технические требования так, что вопрос распределения данных по таблицам может, в большей степени, относится к решению этих технических проблем, а не к соответствию 1 в 1 с Вашей моделью данных. То, что у сотруника в разных контекстах есть как общие атрибуты так и частные напоминает наследование. А для наследования придумали различные способы хранения данных в таблицах, такие как Single Table Inheritance, Class Table Inheritance и Concrete Table Inheritance. В Вашем случае вполне может быть подходящим второй способ. Тогда Вы сможете определить и выделить общие черты нужных сущностей и положить их в одну таблицу позволив разным командам создать таблицы для атрибутов этой сущности относящихся только к их задачам. Или даже третий способ для начала, чтобы быть полностью независимыми друг от друга. У каждого подхода будут как свои плюсы так и минусы как в плане добавления\удаления атрибутов так и в плане взаимопересечения команд и дублирования данных. Независимо от варианта со структурой таблиц стоит использовать дополнительный слой абстракции над этими таблицами в виде представлений. Такой подход позволит склеить разные или разделить одну таблицы. В дальнейшем, когда сама модель данных перестанет сильно меняться, можно будет рассмотреть первый вариант. Это должно будет упростить логику приложения и, возможно, снизить размер сохраненного состояния. Ну и когда над одной БД работают различные группы людей особенно с разным видением я думаю, что стоит в первую очередь выработать какие-то общие правила (стандарты) как минимум в области именования обьектов.

Поиск по денормализованным данным

#база_данных #архитектура


Есть некоторая сущность типа "конференция", в которой могут оказаться несколько участников
с разными тегами:

{
  "participants": {
    "a": "79219998877",
    "b": "79219998878",
    "c": "79219998879"
  }
}


Мне необходимо хранить эти данные в БД (конкретный движок сейчас неважен - он, вероятнее
всего, сменится через некоторое время - считаем, что нет ни джойнов, ни индексации
полей-массивов) и производить по ним поиск - мне надо получать конференции, удовлетворяющие
произвольному количеству условий "a был 79219998877", "b был 79219998878" и аналогам,
a и b при этом могут оказаться абсолютно произвольными тегами. Можно ли как-то представить
эти данные в каком-то виде (без полей-массивов и джойнов), позволяющем производить
подобный поиск простой выборкой, или это в приницпе невозможно?
    


Ответы

Ответ 1



У вас некорректная постановка задачи. Как мне хранить такие данные, чтобы быстро и безболезненно осуществлять поиск по произвольному количеству участников? Но при этом Мне необходимо хранить эти данные в БД (конкретный движок сейчас неважен - он, вероятнее всего, сменится через некоторое время - считаем, что нет ни джойнов, ни индексации полей-массивов) и производить по ним поиск. Ответ будет зависеть от типа БД, которую вы выберите. Самый "безболезненный" вариант - храните все в JSON и Filter вам в помощь. Но такой самый простой вариант вас, скорее всего, не устроит, потому что есть еще другие данные, которые будет неудобно хранить в таком виде и болезненно использовать. При выборе NoSQL хранилища у вас почти полный контроль над данными и выборкой, но за это сами следите за консистентностью данных. Но зато можете применять любой Filter, который выберет нужные вам записи. И если дальше в проекте у вас будут сущность с большим количеством связей, то можно замучаться следить за ними. В случае реляционной БД, все сложнее, потому что на сложность фильтра ложатся ограничения реляционной алгебры, зато все в порядке с консистентностью и выборке по индексу. Фильтр из примера можно реализовать чем то типа: http://sqlfiddle.com/#!9/b891f SELECT Conference.conf_id, COUNT(Conference.conf_id) as cnt FROM Conference JOIN Participants ON Conference.conf_id = Participants.conf_id AND (Participants.key, Participants.val) IN ( ('a', '79219998878'), ('b', '79219998877') ) GROUP BY (Conference.conf_id) HAVING cnt = 2 Если что-то более сложное, то скорее всего ограничения реляционной алгебры не позволят вам это сделать. Но и из этой ситуации есть, как вам подсказали, выход - хранить вашу структуру JSON'ом и накладывать условие на выборку SELECT * FROM Conf WHERE JSON_CONTAINS(json_field, "79219998877", "$.a") AND JSON_CONTAINS(json_field, "79219998878", "$.b") -- или через виртуальные колонки -- WHERE json_field->"$.a" = "79219998877" AND json_field->"$.b" = "79219998878" виртуальные колонки Так вы получите гибрид обоих миров, но доступно только в MySQL > 5.7. Там даже можно индексы строить. UDP: напомнили про postgres, спасибо Да, там тоже есть подобный функционал с jsonb и тоже можно по полю строить запросы. Смотрите @> и <@ операторы. UDP: после прочтения комментов, мне более менее стало ясно, что имелось в виду В данный момент проект сидит на реляционке, в будущем планируется переход на row-column хранилище (NoSQL - это далеко не только json-подобные хранилища), которая предполагает подготовку данных для простых запросов в "плоском" виде, и в котором я не смогу искать по части ассоциативного массива (только по полной версии). Отсюда произрастает вышеописанный вопрос, который сводится не к поиску обходных решений, а к тому, можно ли как-то представить все это в подготовленном виде. В данном случае "обходное" решение это своя собственная реализация индекса по массиву документов. Но написание "своего" такого индексатора - задача очень сложная. Вы же не сможете встроить этот индексатор в свою БД. А его реализация на php/ruby/python, скорее всего, будет уступать по производительности полному перебору в базе. Значит придется писать его на "системном" языке и общаться посредством IPC/socket. Тогда способ как им пользоваться я вижу так: при создании/удалении документа вы будете передавать индексатору этот документ, а он будет на этом основании изменять индекс. Потом когда вам потребуется выборка, вы делаете к нему запрос, а вам он быстренько возвращает ID элементов удовлетворяющих этому запросу, и уже с этими ID вы лезете в базу и выбираете записи. Тогда зачем изобретать этот велосипед, если можно поднять какую-нить MongoDB и пользоваться ей точно так же. Дальше пример для Mongo. Документы хранить лучше в виде: { "_id" : ObjectId("571923c7e4b08c60be5228a4"), "id" : 1, "participants" : [ { "key" : "a", "value" : "79219998878" }, { "key" : "b", "value" : "79219998877" }, { "key" : "c", "value" : "79219998879" } ] } { "_id" : ObjectId("571923f0e4b08c60be5228a9"), "id" : 2, "participants" : [ { "key" : "a", "value" : "79219998877" }, { "key" : "b", "value" : "79219998878" } ] } { "_id" : ObjectId("57193370e4b08c60be522acb"), "id" : 3, "participants" : [ { "key" : "a", "value" : "79219998877" }, { "key" : "c", "value" : "79219998879" } ] } { "_id" : ObjectId("571933c2e4b08c60be522ad4"), "id" : 4, "participants" : [ { "key" : "a", "value" : "79219998878" }, { "key" : "b", "value" : "79219998877" }, { "key" : "d", "value" : "79219998873" } ] } Сделать индекс: db.participants.createIndex({ "participants.key" : 1 , "participants.value" : 1}) И искать: db.participants.find( { "participants" : { "$all" : [ { "$elemMatch" : { "key" : "a", "value" : "79219998878" } }, { "$elemMatch" : { "key" : "b", "value" : "79219998877" } } ] } } ).pretty() Вывод { "_id" : ObjectId("571923c7e4b08c60be5228a4"), "id" : 1, "participants" : [ { "key" : "a", "value" : "79219998878" }, { "key" : "b", "value" : "79219998877" }, { "key" : "c", "value" : "79219998879" } ] } { "_id" : ObjectId("571933c2e4b08c60be522ad4"), "id" : 4, "participants" : [ { "key" : "a", "value" : "79219998878" }, { "key" : "b", "value" : "79219998877" }, { "key" : "d", "value" : "79219998873" } ] } Если сделать explain(), то будет видно что используются индексы. "winning plan": { "inputStage" : { // INDEX SCAN!!! "stage" : "IXSCAN", "keyPattern" : { "participants.key" : 1, "participants.value" : 1 }, "indexName" : "participants.key_1_participants.value_1", "isMultiKey" : true, "direction" : "forward", "indexBounds" : { "participants.key" : [ "[\"a\", \"a\"]" ], "participants.value" : [ "[\"79219998878\", \"79219998878\"]" ] } } И просто пользуетесь ей как внешним индексатором. Да, есть оверхед, что ради этого индексатора вам придется запускать целую монгу. Но и передавать вы можете не целый документ, а только ID и массив участников. Думаю в других NoSQL БД тоже есть индексатор с требуемым функционалом и, возможно, они "легче" монги, можете использовать их. Если уж очень хочется, то можно покопаться в исходниках монги, понять как работает такой индексатор и переписать самому. Но, по моему, оверхед на монгу дешевле чем реализация велосипеда на стероидах.

Ответ 2



Можно создать таблицу с полями a, b, conf_id, conf_date и делать select. Идеально, если можно было бы создать еще и индекс по a и b. Без него будет долго, однако шустрее, чем прямой поиск по таблице с конференциями. Без индекса см. вариант 3. Если нет индексации полей, но есть сортировка и возможность быстрого создания таблиц, можно вместо индекса создавать таблицы с названием simulated_index_{a}_{b}, по сути иммитация индекса. Кстати, работать будет едва ли не шустрее, чем в первом случае с индексами. Если есть только возможность сортировки по произвольному полю и нет индекса, то тогда можно взять вариант 1, но добавить поле ab, по которому будем сортировать. ab = a*2^32 + b (тогда ab будет int64, 2^32 - размер int). Или ab = md5(a) ^ b . Ну то есть надо как-то объеденить два поля, а потом на лету вычислять нужное значение. Если всё надо держать в одной таблице (которая выше), но к этой таблице можно добавлять колонки, тогда просто добавляем колонку ab из примера 3. Я уверен, есть еще варианты. Пишите, если эти не подходят.

Ответ 3



Вариант 1. Если форма данных в виде JSON не принципиальна, то в MSSQL можно использовать дататип XML. Подробней, это очень объемная тема, можно почитать здесь или, например, здесь. К сожалению поддержку самого JSON - MS обещает только в версии 2016. Вариант 2. В MySql дататип JSON уже реализован (почитать можно здесь) что позволит вам просто хранить в поле эту конференцию и производить поиск. Однако я мало работал с MySQL и по работе с ней вряд ли подскажу что полезное. Но покопать в эту сторону вам, вероятно, будет интересно.

суббота, 11 января 2020 г.

Как сделать код контекстно-зависимым, но при этом не передавать контекст явно?

#c_sharp #архитектура


Что есть сейчас:

public static Page GetPage(Uri url)


Что нужно:

Ограничить число запросов в рамках определенных "контекстов". 


Это может быть host в параметре url (и это самый просто вариант, можно просто записывать
текущие хосты и ограничивать)
Может быть "сайт", который хостит разные вещи на разных инстансах облачного провайдера
и по хосту их уже не отличить.
Может быть сборка запрашивающая метод (т.е. сборке например нельзя этим методом получать
больше чем 3 разных страницы в один момент времени)
Может быть метод (метод не должен например параллельно делать большую пачку запросов),
хотя возможно входит в предыдущий вариант, не продумывал пока детально.


В целом, с одной стороны хочется чтобы когда я делаю условный godObject.Download(),
а тот делает какие то свои проверки внутри, плюс скачивание, плюс ещё чего-нибудь,
чтобы внутри не возникала куча запросов параллельных, с другой - чтобы когда я делаю
simpleClass.DownloadAllLinks() он мог обработать параллельно пачку ссылок, если на
разных ресурсах находятся.



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

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


Ответы

Ответ 1



Основная идея мне пришла давно и в целом, её реализуемость подтвердило это сообщение К сожалению, в netcore пока нет CallContext, но enSO подсказало выход, который при тестировании вел себя аналогично и никаких проблем не выявил. В итоге, как выглядит код: using (ThrottleService.SetThrottler(new Throttler(15))) { godObject.Download(); } ThrottleService просто ставит в CallContext экземпляр троттлера, а GetPage внутри просто обращается к CallContext и проверяет, как там у него с ограничениями. Отлично сработало с создаваемыми подтасками, внутри троттлера тупо SemaphoreSlim. Из того, что ещё можно сделать - можно вкладывать троттлеры друг в друга, чтобы работали конструкции вида (созданные в разных методах): using (ThrottleService.SetThrottler(new Throttler(15))) using (ThrottleService.SetThrottler(new Throttler(10))) Это решение неплохо заходит с точки зрения прикладной логики - если ты знаешь, что твой код может генерировать много запросов, укажи какое то ограничение. Тут же кроется и минус - если ты ограничение указал в конкретном месте, то выше по стеку можно только ещё больше ограничить условия, передать только своё ограничение уже не получится. Над этим можно подумать и изобрести костыль, но в целом тут скорее прав A K и стоит подумать над рефакторингом. Пока, в целях ускорения решения задачи я скорее скомбинирую код - добавлю и решение с CallContext и добавлю возможность явно передавать троттлер, для случаев когда он и так под рукой имеется.

Ответ 2



Сталкивался с аналогичными задачами в рамках "качаем сайты" или "делаем запросы к СМЭВ" и в общем-то не понимаю сути вопроса. Вам ничего не мешает внутри вашего метода расположить любую логику "не более трёх запроов в секунду на такой-то домен и не более пяти на другой" и ставить в очередь запросы, которые пока невозможно обработать по ограничениям. Ведь вы и сами понимаете положа руку на сердце, что пошли по кривой тропке, где ваш объект всё больше становится god object - и понимаете, что переделка сигнатуры метода уже дастся немалой кровью — но пока не готовы вернутся на правильную дорогу. Что ж, идите дальше — просто дальше цена будет лишь больше. По мне вам просто нужно решиться на нормальный рефакторинг. Видел много людей, которые долго собираются с мыслью пойти к стоматологу, а потом удивляются, чего же это они раньше не пошли. Тут что-то похожее. Вам пока не поздно — отрефакторьте по-уму, самому же потом проще будет, особенно когда будете добавлять отдельные потоки/обработчики, каждый из которых будет свою прокси иметь, свои очереди и полиси. Мне видится это как полноценный объект, который умеет принимать ссылку в очередь и умеет работать с политиками очереди. Ну и пашет себе где-то в отдельном от UI потоке.

четверг, 9 января 2020 г.

Vue как отличать разные типы компонентов

#архитектура #vuejs #чистый_код


У меня есть несколько типов компонентов.


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


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

Думаю что компоненты части страниц (1) просто названиями называть, например Likes.

Компоненты с fetch (2) добавлять в название WithGetData или WithFetch, например ForumThreadPanelWithGetData

Компоненты странички (3) называть c Page префиксом, например ForumPage.

Если (2) и (3) вместе, то просто с Page префиксом делать без WithGetData 

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


Ответы

Ответ 1



Для данных нужен vuex. Если нужно запрашивать данные при переходах, то делать это в хуках роутов. Компоненты - это реактивный DOM. Vuex + API утилиты - это данные. И тогда не будет смысла в таких названиях. В приложении будет один источник данных, один источник "истины". Для сложных приложений (типа форума или магазина) это просто необходимо. Хранилище Vuex "реактивно" и компоненты просто реагируют на изменения в нём. А также могут инициировать изменения (commit, mutations). https://vuex.vuejs.org/ru/ https://router.vuejs.org/en/advanced/data-fetching.html примеры приложений - https://github.com/vuejs/vuex/tree/dev/examples дополнение к комментариям: Хранилище Vuex это состояние приложения. Данные могут сохраняться где угодно, например кэшироваться в LocalStorage. Смысл в том, что на изменения данных могут реагировать разные компоненты одновременно и layout, и page, и navbar, уведомления и т.д. Также хранилище Vuex поддерживает модульность, то есть можно создать отдельные модули для разных сегментов приложения new Vuex({ modules: {...}}). И конечно это не отменяет возможность загрузки данных в компоненте, возможность использовать локальное состояние. Но вы неизбежно столкнетесь с необходимостью уведомлять другие компоненты об изменения состояния. Централизованное хранилище, хоть и выглядит сложновато, помогает решить множество проблем согласованности и значительно упрощает логику приложения.