TeamLead в Solaris
  • 5 книг про менеджмент, презентації, грошики та метрики. А також про нарцисів і психопатів в командах

    Якщо раптом хтось бажає поділитись прочитаною книгою з іншими, то можете скористатись ботом.

    t.me/booksharing_in_ua_bot

  • Що ви читаєте в основному — фікшн чи нон-фікшн?

    Намагаюсь чергувати, фікшн, біографії і професійну літературу.
    Зараз читаю To engineer is human.
    До неї прочитав трилогію Лю Цисіна «Проблема трьох тіл» ... (перша і друга норм зайшли, третя ну дууже затягнута)
    До них читав про Амазон та Спотіфай.
    Ще пробував починати цикл Азімова — Фундація. Але осилив тікі перші дві книжки.

    Якщо хтось хоче теж ці книжки почитати, можете забрати в боті
    t.me/booksharing_in_ua_bot

    Підтримав: Євгенія Невська
  • Коли ШІ погано виконує роботу бекендерів і чи замінить він джуніорів

    От тільки робота джуна це не делівері, а навчання щоб перестати бути джуном.
    Шкода шо в статті не висвітлили як ШІ допомагає їм вчитись ;)

  • Чим замінити Flipper Zero: 12 альтернатив для пентесту

    Користуюсь що дня.

    Цікаво як?

  • Чому Revolut запустився в Україні зараз і які плани щодо найму і нових послуг. Інтерв’ю з топменеджером

    Тобто наразі в рахунок револют можна відправляти лише валюту?
    Гривневих рахунків нема, і операції з конвертації гривні в безготівкову валюту заборонені?

    Чим тоді на данний момент ваш продукт відмінний від того ж Вайзу?

    Підтримав: Nauka Nauka
  • Реалізація підтримки MongoDB для NestJS Boilerplate з використанням гексагональної архітектури

    Я з нестом майже не працював. Допоможіть зрозуміти менеджмент залежностей та інжекшенів.
    В кожній папці модуля якоїсь доменної сутності є опис її ропозиторія в файлі сервіса ми вказуем який класс інжектити, мені з коду не зрозуміло в який момент створюється інстас TypeORM і відкривається конект до самої бази?

    і чи є в коді приклад де декілька сутностей вкладаються в якийсь сервіс і там проходять операції над ними?

  • Місце ORM у загальній архітектурі застосунку

    Прикол в тому шо з моєї практики написання простого телеграм боту Anemic теж виявилась єффективнішою.

    реализация функциональности за меньшие деньги

    я реалізовував тільки те шо було потрібно у моменті, легко вносив суттеві зміни бо елементи системи прості та слабко залежать один від одного.

  • Місце ORM у загальній архітектурі застосунку

    Почну ще один тред. Бо в обговореннях куча теорії та маловато конкретики. А до кучі ще й вагон різноманітних термінів які чи то синоніми чи то ні.

    На вашому реальному досвіді скільки було проєктів де доступ до бази був через жорску абстракцію без протікання ORM?

    В мене 2.

    Бо теорію всі знають та статті читають. А на практиці часто виходять шо як навчились MVC штампувати то так все життя і штампують.

  • Місце ORM у загальній архітектурі застосунку

    Если ваше приложение простое, то anemic.

    от це іде в розріз з думкою більшості.

    адже загальний підхід: якшо в тебе простий круд то використовуй Rich Domain Model

    чи я не вірно зрозумів шо є шо?

  • Місце ORM у загальній архітектурі застосунку

    А то я такого коду не бачив) ...

    зрозумів вашу позицію, дякую що поділилсь досвідом.

  • Місце ORM у загальній архітектурі застосунку

    В якому домені робив би, в якому ні?

  • Місце ORM у загальній архітектурі застосунку

    В мене викристалізовалось питання до тих хто ще читає цю статтю.

    Чистий проект.
    Треба прицняти рішення зразу, використовувати протікання ORM чи починати писати абстракцію Datagateway\Data Access Layer. Скоуп проєкту взагалі не зрозумілий. Не ясно окупляться чи ні затрати часу на написання Datagateway.

    З закритими очима, який підхід обиреш?

  • Місце ORM у загальній архітектурі застосунку

    Витягувати всю ієрархію

    як ти зміг пекласти getALLOpenSubtasks у витягнути всю іерархію?
    я ж написав шо там може бути логіка ідентична циклу у markDone.

    Ніякого TDD не було

    ок, тобі видніше .... менше з тим, тема дискуссії не TDD

    Ще тобі треба в залежності від флага force викликати getALLOpenSubtasks чи getJustDirectSubtasks, ну і оптимізувати перший метод, щоб він не тягнув геть усе.

    знову ні, в storage я створив спочатку функцію яка повертає тільки таску, і додав функцію шо повертає усі не закриті сабтаски, з пошуком у глиб. я думав імена функцій кажуть самі за себе.
    для якого юзкейсу потрібен getJustDirectSubtasks?

    Ти вільно апдейтиш статус.

    так а в чому проблема то? юзкейс зміни статуса у нашому застосунку лише в одному місці, обробці POST /task/status . я цей обробник реалізував шоб він виконував те шо просить продакт овнер. Аксептенс критерї проходе, усі варіанти подій при зміні статусу покриті в тест сюті TaskService.changeStatus. Ну чим ти ще не задоволений?

    Слово «інкапсуляція» залишилось на співбесіді, її — нема.

    Інкапсуляція заради інкапсуляції?

    це суміш бізнес-логіки

    дивлячись шо ти вважаєш бузнес логікою.
    От продакт каже:
    WHEN Task DOES status change TO Done
    IF any not done subtask in hierarchy AND no force THEN throw error
    IF any not done subtask in hierarchy AND force THEN main task status done and all subtasks in herarchy status done

    то TaskService.changeStatus повністю описує юзкейс цєї бізнеслогіки.
    з моє точки зору нічого не розмазано. якшо я хочу подививтись як веде себе Task, я відкриваю його інтерактор і вся бізнеслогіка перед очима.

  • Місце ORM у загальній архітектурі застосунку

    ф забув додати, шо поки сіньйор-помідор гуглить як робити SQL-пошук по графу, джун штампує тести для TaskService і робить їх зелененькими

  • Місце ORM у загальній архітектурі застосунку

    фікс, у тесті має бути
    service = new TaskService(mockStorage)
    service.changeStatus(1, Done, true)

  • Місце ORM у загальній архітектурі застосунку

    Не зрозумів. Про які декоратори йде мова?

    оці:
    @Entity
    @Table(name = «tasks»)
    @Getter
    @Slf4j
    @ManyToOne(fetch = FetchType.LAZY)
    @OneToMany(mappedBy = «parent», cascade = CascadeType.ALL, orphanRemoval = true)

    я буквально казав спробуй розвернути код усьої логіки.
    З таким кодом можеш процювати тільки ти або якійсь мідл+. Джун без досвіду з Hibernate нічого зробити не зможе.

    Це гарне питання, я буду передавати лямбду/колбек/consumer в updateStatus метод.

    і як це вплине на твій існуючий тест markDoneForce?
    а якшо в тебе тест сют та класс Task з сотнею тестів, в ще є тести інших сутностей де таск використовується?(це я вже перегібаю)

  • Місце ORM у загальній архітектурі застосунку

    Приходить продакт овнер і каже, в нас міні чендж реквест, вкладеність тепер нескінченна.
    Я вже пообіцяв, зробіть на завтра.

    class Task { 
        constructor(id:number, parent?:number, status:TaskStatus, subtasks?:Task[]){ ... }
        id:number  
        parent:number  
        status:TaskStatus 
        subtasks:Task[] 
    } 
    
    Storage{
        getALLOpenSubtasks(parentID):Task[] { 
            // SQL пошук я зараз не придумаю але думаю що це реально, 
            //як мінімум в циклі шукати  через декілька запитів
           //по навантаженню на базу це буде те саме шо твій лейзі лоад у циклі private void markDone
    
            foundTasksWithNotDoneStatus:Task[] = mapper(dataFromORM)
    
            return foundTasksWithNotDoneStatus
        }
    }
    
    можем тестити на реальній локальній базі, заповненій будь якими тестовими данними

    тепер троти TDD inda house, можем хоч усі юзкейси описати, але зупинемось на одному

     
    mockStorage ={
        getTask:jestMockFunction()
        getALLOpenSubtasks:jestMockFunction()
    }
    
    it('should change status of all subtasks to Done', () => {
        main = new Task(1, null, TaskStatus.InProgress, [])
        sub1 = new Task(1, 1, TaskStatus.InProgress)
        sub1 = new Task(2, 1, TaskStatus.New)
    
        mockStorage.getTask.mockReturnValue([main])
        mockStorage.getALLOpenSubtasks.mockReturnValue([sub1, sub2])
    
        service = new TaskService(mockStorage)
        service.changeStatusOfAllSubtasksToDone(1)
    
        expect(main.stauts).toEqual(Done)
        expect(sub1.stauts).toEqual(Done)
        expect(sub2.stauts).toEqual(Done)
        expect(mockStorage.bulkTaskSave).toHaveBeenCalledWith([main, sub1, sub2])
    })
    

    тереп робимо тест зелененьким

    class TaskService{
    
     changeStatus(taskID:number, status:TaskStauts, forcce?:bool){
            task = this.storage.getTaskOnly(taskID)
            if(!task) throw new Error('Task not found')
    
    
            if(status==TaskStatus.New || status==TaskStatus.InProgress){
                task.status= status
                this.storage.saveTask(task)
            }else if(status == TaskStatus.Done ){
    
                undoneSubs = this.storage.getALLOpenSubtasks(taskID)
                if(undoneSubs.length>0 && !force) throw new Error('Subtasks not done')
    
                tasks.status= status
                doneSubs = undoneSubs.each(subtask=>subtask.status=status)
    
                this.storgeUpdateTaskBulk([tasks, ...doneSubs])
            }else{
                throw new Error('Unknown status')
            }
        }
    
    }
    

    Ось і все, час купляти сир чи слати донат Чмуту.
    Зверни увагу як мало мені довелось змінити в порівнянні з попереднім рішенням.

  • Місце ORM у загальній архітектурі застосунку

    Поправочка

    Service — це інтерактор, тут описуються юзкейси маніпуляцій над таском,

    над таском, або над таском та іншими сутностями повязаними цим юзкейсом.

  • Місце ORM у загальній архітектурі застосунку

    І як побороти перевантаження Data Access Layer навалою різних методів типу:
    getTask
    getTaskWithSubs
    getTaskForAutocompit
    getAggergatedTask
    .....
    і ТД.

    мож знаєшь гарні статті про best-practices як його структурвати?

    POD

    шо за акронім?

  • Місце ORM у загальній архітектурі застосунку

    Ем, а що тут поганого-то?

    Спробуй розверни усі декоратори які навішані на класс таски, побачиш наскільки перегружений логікою цей обєкт. Чим складніший класс — тим важче його змінювати в майбутьному.

    Для чого два класи на одну бізнес-сутність?

    Шоб позбутись тої проблеми коли завантажеється лишня інфа де її не треба. Просто з моєї точки зору то дві різні бізнес сутності які затребувані в різних бізнес юзкейсах.
    В твоїй реалізаціїї, клас = таблиці. і це якраз той coupling про який я казав, система більш гнучка коли це не так.

    Під lazy-loading-ом (у контексті ORM) я мав на увазі, що замість завантаження даних в properties створюються проксі, які відпрацьовують при звертанні до тих.

    Догнав. ІМХО це страшне зло, але то вже персональні вподобання.

    Про розділення логіки. ХЗ як краще, але зазвичай якшо це суто властивостей класу сутності — то в клвсі сутності. Якшо це про маніпуляції над сутнісю чи між декількома — то в інтеракторі.
    Service — це інтерактор, тут описуються юзкейси маніпуляцій над таском, які від нас просить продактовнер.

    Питання — скільки даних завантажити з БД для цього?

    Так само в обох варіантах ... чи я шось не розумію.

    Class TaskService{   
    
        changeStatus(taskID:number, status:TaskStauts, forcce?:bool){
    
            if(status==TaskStatus.Done || status==TaskStatus.InProgress){
                task = this.storage.getTaskOnly(taskID)
                if(!task) throw new Error('Task not found')
    
                task.status= status
                this.storage.saveTask(task)
            }else if(status == TaskStatus.Done ){
                taskWithSubs = this.storage.getTaskWithSubs(taskID)
                if(!task) throw new Error('Task not found')
    
                isUndoneSubs = taskWithSubs.subtasks.some(subtask=>subtask.status!=TaskStatus.Done)
                if(isUndoneSubs && !force) throw new Error('Subtasks not done')
    
                taskWithSubs.status= status
                subsToUpdate = taskWithSubs.subtasks.find(subtask=>subtask.status!=TaskStatus.Done).each(subtask=>subtask.status=TaskStatus.Done)
                this.storgeUpdateTaskBulk([taskWithSubs, ...subsToUpdate])
            }else{
                throw new Error('Unknown status')
            }
    
        }
    }
    

    Просто в мому варіанті логіка в інтеракторі, і у випадку якшо при зміні статусу таски треба ще якісь діії виконувати, наприклад відправити листа асайні сабтаски, то це леко додати. Не треба тягнути у класс таски всю логіку відправки листів.

← Сtrl 12 Ctrl →