Демоприложение Один плюс один, разработка ПО Логотип

Как создать новое приложение на основе шаблона

Каноническое руководство — для людей и для Claude. DemoAppCore — это исходный код самой методики: каждое новое приложение компании «Один плюс один» начинается как его копия.

1 · Начало работы

  • Копируем структуру решения: DataAccessLayer, BusinessLogicLayer, UI и веб-приложение (на базе DemoApp).
  • DAL и UI копируются байт в байт — это канон, и менять их под отдельный проект запрещено. Нужна общая правка? Сначала здесь, в шаблоне.
  • Имя проекта, namespace, логотип и заголовок меняются только в самом приложении.
  • У каждого приложения свой порт разработки (launchSettings) — порты между проектами не дублируются.
  • Есть готовый автоматический скил: new-app-from-demoappcore.

2 · Новая сущность (BLL)

public class Invoice : Record
{
    [Column(Size = 300)]
    public override string Description { get; set; }
    [Column] public double Amount { get; set; }
    [Column] public DateTime Issued { get; set; }
    [Column] public bool IsPaid { get; set; }
}
  • Таблица создаётся и обновляется сама в SetupAsync — никакого SQL и никаких миграций.
  • Имена таблиц — в единственном числе. DateTime.MinValue = дата отсутствует.
  • Сущность, за которой нет реальной таблицы (демо, отображение): [Table(Ignore = true)].
  • Начальные данные (seed): переопределение CreateAsync в сущности (как в Settings).

3 · Экран управления

// DemoApp/Tables/InvoicesTable.cs
public class InvoicesTable : TableMakerEx<Invoice>
{
    public InvoicesTable()
    {
        this["Description"].HeaderText = "Описание";
        this["Amount"].HeaderText = "Сумма";
        this["IsPaid"].HeaderText = "Оплачено";
        this["IsPaid"].Filterable = true;
    }
}

// Pages/Invoices.html
<h1>Счета</h1>
<div id="InvoicesTable" class="table"></div>

// Code/App.cs
public ReturnDetails Invoices() => AdminPage("Invoices");

// Pages/Main.html — кнопка в меню
<input type="button" id="Invoices" value="Счета" />

Это полноценный экран: таблица, сортировка, фильтрация, форма, экспорт. Без JS и без SQL.

4 · Страницы без таблицы

  • Страница = HTML-файл в wwwroot/Pages + метод в App, который возвращает ReturnDetails.
  • Кнопка вызывает метод по своему id — без onclick и без новой конечной точки.
  • Параметры со страницы приходят в Data (словарь); ответ сервера может передать html, popUp, script или наполнение списков.
  • Файлы Pages/ закрыты для прямого обращения из браузера — они отдаются только через ButtonClicked.
  • Состояние приложения (свойства App) сохраняется между вызовами — в зашифрованном и сжатом виде на стороне клиента.

5 · Контент, SEO и многоязычность

  • Страница контента = запись (ContentPage), а не файл: человекопонятный адрес, H1, отдельный title, описание, изображение для соцсетей и структурированные данные. Редактируется на экране «Страницы контента».
  • Серверный рендеринг каждой публичной страницы (Code/Seo.cs): уже первый ответ содержит контент и полный <head>. Панель управления остаётся SPA и получает noindex.
  • Новый экран, который должен попасть в индекс: добавьте его в Seo.PublicMethods и задайте ему PageMeta в PageMeta.Screens. Экран панели управления — в AdminMethods.
  • Сохранённые и вычисляемые метаданные проходят через единый интерфейс — IPageMeta. Динамически созданная страница строит PageMeta.For("/…").Titled(…) во время выполнения.
  • Любое изменение адреса требует записи в «Перенаправлениях» (PageRedirect), иначе каждая внешняя ссылка молча ломается. Старый числовой адрес (/Page@41) остаётся рабочим навсегда.
  • sitemap.xml и robots.txt строятся из данных сами; элементы интерфейса переводятся токенами ({текст}Word), а объёмные тексты пишутся отдельной записью для каждого языка.
  • slug всегда на английском, строчными буквами и через дефис. Язык по умолчанию — в корне, остальные языки под /en/; теги hreflang выводятся сами из общего Identify.
  • В продакшене заполните SiteBaseUrl в настройках — на нём держатся canonical и карта сайта.

6 · Обязательные правила кода

  • reuse-first: перед любой разработкой проверяем, что уже есть в Util, DAL и UI.
  • Только асинхронно: синхронных обращений к базе данных нет; async доходит до самого контроллера.
  • Точечное обновление: UpdatePropertiesAsync("Col"), а не полное сохранение.
  • Оформление только в CSS, размеры в rem; изменили CSS/JS ⇐ поднимите ?v=.
  • Settings — в базе данных, а не в appsettings; база данных для разработки = localhost/SQLite.
  • Поля не на иврите (электронная почта, телефон) — LTR.
  • Фоновые задачи обязаны допускать возобновление (resumable).

7 · Перед сдачей работы

  • Чистая сборка — без единого оставшегося предупреждения.
  • code-review по diff + второе мнение (Gemini).
  • Настоящая дымовая проверка в браузере — весь сценарий целиком, а не только компиляция.
  • commit в SVN с содержательным сообщением; файлы .md тоже попадают в репозиторий.
  • Развёртывание: переносимая папка (self-contained); в PROD — только с явного разрешения.