Нарушение принципов SOLID и другое

Задача:

В таблице RawLinks [ID,Link] скапливаюься необработанные (с мусором) ссылки на подобии этой 325230fkewf ru.stackoverflow.com/users/373567/ odwf3430. Необходимо определять их тип, нормализовать в соответствии с типоп, проверять по базе уже существующих ссылок, если нет, сохранить.

Cамо решение задачи не интересует, она решается и так и сяк.

Нужна помощь с выделением сущности, с принципами SOLID

Загвоздка в понимании как лучше:

  • Нужно использовать 1 какую то сущность сохраняя её состоянии по разным таблицам ?
  • Или может быть нужно иметь объекты под все ситуации, типа RawLink, DefinedLink, NotDefinedLinks и т.д.
  • Или может сущность и DTO
  • Или вообще всё как то иначе

Код 1 из 3 подобных микро компонентов

public class StackOverflowLink
{
    public string Link { get; set; }
}

public class TypedStackOverflowLink : StackOverflowLink
{
    public TypeStackOverflowLink Type { get; set; }
}

public class Definer(IDataSource<StackOverflowLink> dataSourceRawLink, IDataReceiver<TypedStackOverflowLink> dataReceiverDefinedLink, IDataReceiver<StackOverflowLink> dataReceiverNotDefinedLink)
{
    public async Task StartAsync()
    {
        while (true) 
        {
            try
            {
                await UnitWorkAsync();
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }

            await Task.Delay(1000);
        }
    }

    // Открыт для интеграционного теста
    public async Task UnitWorkAsync() 
    {
        StackOverflowLink rawLink = await WaitGetRawLinkAsync();

        TypeStackOverflowLink typeStackOverflowLink = DefineType(rawLink);

        await WaitReceiveAsync(typeStackOverflowLink, rawLink);
    }

    private async Task<StackOverflowLink> WaitGetRawLinkAsync()
    {
        while (true)
        {
            try
            {
                StackOverflowLink? rawLink = await dataSourceRawLink.GetAsync();

                if (rawLink != null)
                {
                    return rawLink;
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }

            await Task.Delay(1000);
        }
    }

    private async Task WaitReceiveAsync(TypeStackOverflowLink typeStackOverflowLink, StackOverflowLink rawLink) 
    {
        while (true)
        {
            try
            {
                switch (typeStackOverflowLink)
                {
                    case TypeStackOverflowLink.User:
                    case TypeStackOverflowLink.Question:
                        await dataReceiverDefinedLink.ReceiveAsync(new TypedStackOverflowLink()
                        {
                            Link = rawLink.Link,
                            Type = typeStackOverflowLink
                        });
                        break;
                    case TypeStackOverflowLink.Unknown:
                        await dataReceiverNotDefinedLink.ReceiveAsync(rawLink);
                        break;
                    case TypeStackOverflowLink.None:
                    default:
                        throw new ArgumentException($"Неожиданный тип enum typeStackOverflowLink = {typeStackOverflowLink}");
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
        }
    }

    private TypeStackOverflowLink DefineType(StackOverflowLink stackOverflowLink)
    {
        if (Regex.IsMatch(stackOverflowLink.Link, @"ru\.stackoverflow\.com/questions/\d+/"))
        {
            return TypeStackOverflowLink.Question;
        }
        else if (Regex.IsMatch(stackOverflowLink.Link, @"ru\.stackoverflow\.com/users/\d+/"))
        {
            return TypeStackOverflowLink.User;
        }
        else
        {
            return TypeStackOverflowLink.Unknown;
        }
    }
}

PS. Хотел написать вопрос касально проекта из связки независимо работающих 3х компонентов, но он через чур громоздкий для поста( и такое наверно даже никто не прочитает, поэтому попробую по чуть чуть.


Ответы (1 шт):

Автор решения: CrazyElf

Скорее всего вы имели в виду принцип "single responsibility" из SOLID. Но вообще тут скорее нужно исходить из того, как оптимально спроектировать БД под эту задачу. Если я правильно понимаю, если отсеять всю шелуху, у вас есть исходные сырые ссылки и почищенные, проверенные, типизированные чистые ссылки. Ну вот я бы и сделал две таблицы в БД, условно назовём:

  • RawLinks
  • TypedLinks

В первой таблице как сами сырые ссылки, так и необходимые данные для работы задачи по очистке ссылок. Фактически, это таблица задач.

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

Зачем именно две таблицы. Просто чтобы задачи, которые работают с чистыми ссылками быстрее читали данные. Как вариант, вторая таблица может быть "витриной" или копией "очищенной" ссылки, лежащей в первой таблице. Тут нужно смотреть - кто у вас этими "очищенными" ссылками пользуется и насколько ему критична скорость чтения из БД, нужна ли вообще тут эта оптимизация с разбивкой на две таблицы.

С первой же таблицей всё понятно. Единая таблица из обработанных и ещё необработанных сырых ссылок нужна, чтобы у вас не было дублей, когда вы туда новую необработанную ссылку будете добавлять. Ну и нужен признак обработанности. И я бы ещё добавил поле "следующее время обработки", если ссылки могут быть проблемными и скачиваться/обрабатываться не всегда с первого раза. Для таких ссылок это время переносится в будущее обработчиком этих ссылок.

Ну вот как-то так. То есть по сути получается две таблицы с единственной ответственностью:

  • Обработка сырых ссылок
  • Хранение готовых ссылок

Как именно хранить готовые ссылки разных типов, в одной таблице, или делить на несколько - опять же зависит от того, сколько этих типов, сколько записей в таблице и т.д. Т.е. опять же смотрим с точки зрения оптимизации работы с БД. Если там миллиарды записей каждого вида, которые сервис должен непрерывно лопатить, то лучше разделить на несколько таблиц. А если записей немного и сервис не критичный по времени на чтение/фильтрацию этих записей, то можно и в одну таблицу свалить.

Не уверен, что сильно помог, но рассуждать можно примерно так.

→ Ссылка