Я работаю с Google App Engine и использую Java-API низкого уровня для доступа к Big Table.Я создаю приложение SAAS с 4 слоями:
- Клиентский веб-браузер
- Уровень ресурсов RESTful
- Бизнес-уровень
- Уровень доступа к данным
Я создаю приложение, которое поможет мне управлять моей компанией, специализирующейся на автообработке мобильных телефонов (и другим, как она).Я должен представить эти четыре отдельные концепции, но не уверен, что мой текущий план хорош:
- Назначения
- Позиции
- Счета
- Платежи
Назначение: «Назначение» - это место и время, когда сотрудники должны находиться для оказания услуги.
Позиция: «Позиция» - это услуга, плата или скидка и связанная с ней информация.Пример позиций, которые могут попасть на встречу:
Name: Price: Commission: Time estimate
Full Detail, Regular Size: 160 75 3.5 hours
$10 Off Full Detail Coupon: -10 0 0 hours
Premium Detail: 220 110 4.5 hours
Derived totals(not a line item): $370 $185 8.0 hours
Счет-фактура: Счет-фактура - это запись одной или нескольких позиций, за которые клиент обязался оплатить.
Платеж: «Платеж» - это запись о поступлении платежей.
В предыдущей реализации этого приложения жизнь была проще, и я лечил всечетыре из этих понятий как одна таблица в базе данных SQL: «Назначение».Одна «Встреча» может иметь несколько позиций, несколько платежей и один счет.Счет-фактура представлял собой просто электронное письмо или распечатку, созданную из позиций и записей клиентов.
9 из 10 раз, это работало нормальноКогда один клиент записался на прием на один или несколько автомобилей и оплатил их самостоятельно, все было грандиозно.Но эта система не работала во многих условиях.Например:
- Когда один клиент назначил одну встречу, но на полпути назначил встречу, в результате чего на следующий день пришлось вернуться, мне потребовались две встречи, но только одна позиция,один счет и один платеж.
- Когда группа клиентов в офисе решила сделать все свои машины в один и тот же день, чтобы получить скидку, мне потребовалась одна встреча, но несколько счетов и несколько платежей.
- Когда один клиент заплатил за две встречи одним чеком, мне потребовалось две встречи, но только один счет и один платеж.
Я смог справиться со всеми этими выбросами, выдумывая вещинемного.Например, если один из сотрудников должен был вернуться на следующий день, я бы просто назначил еще одну встречу на второй день с позицией с надписью "Готово", а стоимость составила бы 0 долларов.Или, если бы у меня был один клиент, заплативший за два приема с одним чеком, я бы поместил разделенные записи о платежах в каждое назначение.Проблема в том, что это создает огромную возможность для несоответствия данных.Несоответствие данных может быть серьезной проблемой, особенно для случаев, связанных с финансовой информацией, такой как третий пример, когда клиент оплатил две встречи с одним чеком.Платежи должны быть сопоставлены непосредственно с товарами и услугами, предоставляемыми для надлежащего отслеживания дебиторской задолженности.
Предлагаемая структура:
Ниже приведена нормализованная структура для организации и хранения этих данных. Возможно, из-за моей неопытности я уделяю большое внимание нормализации данных, потому что это кажется отличным способом избежать ошибок несоответствия данных. С этой структурой изменения данных могут быть сделаны с одной операцией, не заботясь об обновлении других таблиц. Однако для чтения может потребоваться многократное чтение в сочетании с организацией данных в памяти. Позже я пойму, что если есть проблемы с производительностью, я могу добавить некоторые денормализованные поля в «Назначение» для более быстрого запроса, сохраняя при этом «безопасную» нормализованную структуру нетронутой. Денормализация потенциально может замедлить запись, но я подумал, что смогу сделать асинхронные вызовы к другим ресурсам или добавить в очередь задач, чтобы клиенту не приходилось ждать дополнительных записей, которые обновляют денормализованные части данных. .
Таблицы:
Appointment
start_time
etc...
Invoice
due_date
etc...
Payment
invoice_Key_List
amount_paid
etc...
Line_Item
appointment_Key_List
invoice_Key
name
price
etc...
Ниже приведен ряд запросов и операций, необходимых для связывания всех четырех сущностей (таблиц) для заданного списка встреч. Это будет включать информацию о том, какие услуги были запланированы для каждой встречи, общую стоимость каждой встречи и погоду или не оплату, как было получено для каждой встречи. Это будет обычный запрос при загрузке календаря для планирования встреч или для менеджера, чтобы получить общее представление об операциях.
- QUERY для списка "Встречи", чье поле "start_time" находится между заданным диапазоном.
- Добавьте каждый ключ из возвращенных встреч в Список.
- QUERY для всех "Line_Items", чье поле assign_key_List включает в себя любые возвращаемые встречи
- Добавьте каждый invoice_key из всех позиций в коллекцию Set.
- QUERY для всех «Счета-фактуры» в наборе счетов-фактур (это можно сделать за одну асинхронную операцию с использованием механизма приложений)
- Добавить каждый ключ из возвращенных счетов в Список
- QUERY для всех "Платежей", поле invoice_key_list которого содержит ключ, соответствующий любому из возвращенных счетов
- Реорганизовать в памяти так, чтобы каждое назначение отражало запланированные для него строки line_items, общую стоимость, общее расчетное время и погоду или за нее не было оплачено.
... Как видите, для этой операции требуется 4 запроса к хранилищу данных, а также некоторая организация в оперативной памяти (надеюсь, оперативная память будет довольно быстрой)
Кто-нибудь может прокомментировать этот дизайн? Это лучшее, что я могу придумать, но я подозреваю, что могут быть лучшие варианты или совершенно другие конструкции, о которых я не думаю, что они могли бы работать лучше в целом или конкретно в силу сильных и слабых сторон, возможностей и возможностей GAE (Google App Engine). .
Спасибо!
Уточнение использования
Большинство приложений более интенсивно читают, некоторые более интенсивно пишут. Ниже я опишу типичный пример использования и операции разбивки, которые пользователь хотел бы выполнить:
Менеджер получает звонок от клиента:
- Чтение - Менеджер загружает календарь и ищет доступное время
- Запись - Менеджер запрашивает у клиента информацию, я представлял ее как последовательность асинхронных операций чтения, когда менеджер вводит каждый фрагмент информации, такой как номер телефона, имя, адрес электронной почты, адрес и т. Д. ... Или, если необходимо, возможно, одна запись в конце после того, как клиентское приложение собрало всю информацию, и оно затем было отправлено.
- Запись - Менеджер снимает данные кредитной карты клиента и добавляет ее к своей записи в качестве отдельной операции
- Запись - Менеджер снимает средства с кредитной карты и проверяет, что платеж прошел
Менеджер делает исходящий телефонный звонок:
- Чтение Менеджер загружает календарь
- Чтение Менеджер загружает встречу для клиента, которому он хочет позвонить
- Запись Менеджер нажимает кнопку «Вызов», инициируется вызов и создается новая сущность CallReacordзаписано
- Чтение Сервер вызовов отвечает на запрос вызова и читает CallRecord, чтобы выяснить, как обрабатывать вызов
- Запись Сервер вызовов записывает обновленную информациюв CallRecord
- Запись , когда вызов закрыт, сервер вызовов делает еще один запрос к серверу на обновление ресурса CallRecord (примечание: этот запрос не является критичным по времени)
Принятый ответ :: Оба из первых двух ответов были очень вдумчивыми и оцененными.Я принял тот, у кого было мало голосов, чтобы как можно лучше выровнять их выдержку.