Идея достаточно простая: в пакете в определенной директории создаётся файл php который должен возвращать анонимную функцию обработки следующего вида: Именем функции служит имя файла + поддиректория. Т.е. для файла расположенного в `auth/user/get.php` будет сгенерировано имя `auth_user_get`. В зависимости от первого параметра все функции подразделяются на четыре типа: ApiPut - функция для изменения значений. ApiGet - функция для чтения значений. Результат вызова кешируется до момента изменения зависимостей. К примеру функция возвращает текст статьи по идентификатору. При первом вызове происходит запрос в БД и кеширование результата. При последующих вызовах с тем же идентификатором возвращается кешированное значение. При вызове функции изменения статьи по этому идентификатору происходит сброс кешироемого значения. ApiLifeTime - функция для чтения значений. Результат вызова кешируется до истечении заданного времени. ApiDirect - функция прямого вызова. Для функций чтения (ApiGet и ApiLifeTime) игнорирует кешируемые значения и всегда вызывает заданную функцию. Каждый тип объекта содержит функции, доступные в нем для вызова.   ApiPut - может вывать все функции, кроме функций вида ApiPut. Т.е. функция, изменяющая значение, не может вызвать другую изменяющую функцию. При этом все вызовы будут прямые, т.е. значения в кеше будут игнорироваться. Это связано с тем, что при изменении данных необходимы актуальные значения данных. Вариант такой зависимости рассматривался. Т.е. возможность вызывать функцию вида ApiPut c указанием ключевых полей, что свяжет текущую функцию с другой функцией ApiPut через указанные параметры (но никакого изменения данных не будет!). Это указание системе, что при вызове указанной функции вида ApiPut с такими ключевыми параметрами необходимо сбросить и значения, который зависят от текущей функции. К примеру функция test_get (которая получает данные) зависит от функции test_put (которая изменяет данные). А функция test_put зависит от test_remove (которая тоже изменяет данные). Это значит что при вызове функции test_remove будет сброшено кэширование функций для test_get (test_remove=>test_put=>test_get). Также кэширование для test_get будет сброшено при вызове функции test_put (test_put=>test_get).Однако данный вид зависимости НЕВОЗМОЖНО реализовать так как это требует вызова самой функции изменения, что невозможно. Поэтому от таких зависимостей было решено отказаться. ApiGet - позволяет вызывать функции вида ApiGet. Функции вида ApiDirect недоступны, так как эти функции всегда возвращают разные значения, а значит их нельзя кешировать.  Так как результат кеширования ApiLifeTime изменяется только от времени, то его изменение не приведет к пересчету кешироемого значения в функции вида ApiGet, поэтому вызовы функций вида ApiLifeTime также запрещены.  Так как это функция чтения, то она не может ничего изменять, поэтому функции вида ApiPut в ней доступны, но только в режиме *зависимости*. Т.е. можно вызывать функцию вида ApiPut c указанием ключевых полей, что свяжет функцию ApiGet с функцией ApiPut через указанные параметры (но никакого изменения данных не будет!). Это указание системе, что при вызове функции вида ApiPut с такими ключевыми параметрами необходимо сбросить кешированное в ApiGet значение. К примеру у нас функция получения статьи article_get: Теперь если будет вызывана функция article_put для изменения статьи, то произойдет сброс кешированного значения. ApiLifeTime - позволяет вызывать только функции вида ApiGet и ApiLifeTime.  Итоговая схема вызовов: Для генерации мы ищем все файлы с определенными функциями API и генерируем файл класса для вызова этих функций. Данный файл не является конечным вариантом, а просто иллюстрирует что примерно должно получится. Также генерируются файлы классов ApiPut, ApiGet, ApiLifeTime, ApiDirect. Так как у нас есть список всех функций и их параметров, то сгенерировать такие файлы - это дело техники. При вызове из функции fn_a вида ApiGet функции fn_b вида ApiGet необходимо учитывать что функция fn_a зависит не только от своих связей, но от связей функции fn_b. Т.е. К примеру у нас вот такие зависимости: В этом случае функция fn_b зависит от fn_z. А функция fn_a зависит от fn_z и fn_y (fn_b не учитываем, так как она не может измененять данные). Т.е. при вызове функции fn_z сбросится кешированное значение для функций fn_b и fn_a. А при вызове функции fn_y сбросится кешированное значение только функции fn_a. Идея кеширования основана на докладе Уходим в кэш в высоконагруженных системах / Павел Паршиков (Авито) У каждого ключа есть время установки. Каждый элемент КЕШ-а содержит: Значение. Все ключи от которых зависит значение и время установки этих ключей. При чтении данных из кеша происходит проверка всех зависимых ключей с текущим временем установки этих ключей. Если время какого-то ключа не совпадает, значит значение в КЕШ-е нужно пересчитать. При этом КЕШ хранения данных и КЕШ хранения временных меток ключей может быть разный. Т.к. к меткам времени доступ будет более частый, то имеет смысл хранить их в памяти. Тем более что ключ+метка времени будут занимать не так много памяти даже для большого количества ключей. Рассмотрим более детально. К примеру у нас есть такая цепочка вызовов функций API: При первом вызове в кеше данных устанавливаются следующие значения: Вместе с данными сохраняется время установки значения и время установки всех дочерних вызовов. Во всех случаях она = t1 (хотя на практике значения могут отличаться, но в нашем примере предположим что все временные метки имеют одинаковое значение). Также имеется кеш временных меток. Состояние 1 показывает значения кеша временных меток после первого вызова. При вызове функции установки значения  fn_z происходит сброс временной метки для функции fn_c. Состояние 2 показывает значения кеша временных меток после сброса КЕШ-а функции fn_c.  При запросе значения происходит: 1. Запрос данных из КЕШ-а данных 2. Проверка что временные метки всех дочерних ключей соответствуют тем, что сохранены в КЕШ-е данных в поле rel_keys.  Если временная метка отличается (или отсутствует), то выполняется повторная генерация значения. Если все метки совпали, то значит данные имеют актуальное значение. Храним значение и время до которого это значение валидно. При чтении происходит проверка времени. Если время превышено, то Изменяем время на +30 секунды (значение не важно, главное чтобы оно было больше чем генерируются новые данные). Запускаем функцию генерации новых данных. После генерации новых данных изменяем значение в КЕШ-е. Это позволит уменьшить нагрузку на сервер в случае пересчета данных. Т.е. только первый запрос вызовет их пересчет, остальные будут читать либо уже "продленные", либо новые данные. Все API функции вызываются из соответствующего сервиса: Разработчику не нужно думать о кешировании, достаточно просто написать функцию и указать её тип. Всё остальное будет сгенерировано автоматически. Единственный сервис который необходим для получения данных - это сервис сгенерированного API. В качестве бонуса можно генерировать код для вызова API на frontend-е.  В этом случае одну и ту же функцию, к примеру, для  получения статьи можно использовать как на backend-е, так и на frontend-е. Но тут ещё нужно добавить условие, что все данные, что получает на вход функция и возвращает должны конвертироваться в формат JSON.