Правильная компоновка функциональности в трехслойной архитектуре
Я работаю над проектом asp.net с оракулом в качестве бэкэнда. Изначально я разработал это приложение, используя трехуровневую архитектуру с пользовательским интерфейсом в виде страниц aspx, BLL и DAL в качестве проектов библиотеки классов. Я использовал статические классы и методы как в BLL, так и в DAL. DAL состоит только из одного класса со статическими методами Select, Execute и ExecuteScalar, которые принимают запросы, пересылаемые классами BLL.
DAL
private static string connString = ""
private static OracleConnection conn;
public static OracleConnection OpenConn()
{
if (conn==null)
{
conn = new OracleConnection(connString);
}
if (conn.State != ConnectionState.Open)
{
conn.Open();
}
return conn;
}
public static DataTable Select(string query)
{
DataTable dt = new DataTable();
OracleDataAdapter da = new OracleDataAdapter(query, OpenConn());
da.Fill(dt);
return dt;
}
public static void Execute(string query)
{
OracleCommand cmd = new OracleCommand(query, OpenConn());
cmd.ExecuteNonQuery();
}
public static int ExecuteScaler(string query)
{
OracleCommand cmd = new OracleCommand(query, OpenConn());
int id = Convert.ToInt32(cmd.ExecuteScalar());
return id;
}
Как этот DAL вызывается классами BLL, как показано ниже: Employee BLL
public static DataTable GetEmployees(int facilityid)
{
DataTable dt = new DataTable();
string q = string.Format("SELECT * FROM ..");
dt = OraDAL.Select(q);
return dt;
}
public static DataTable AddEmployee(string name, , int departmentid , int employeeType)
{
DataTable dt = new DataTable();
string q = string.Format("INSERT INTO ...");
dt = OraDAL.Select(q);
return dt;
}
Сейчас я занимаюсь рефакторингом приложения. Та же трехслойная архитектура с BLL и DAL, что и проекты библиотеки классов с дополнительным проектом библиотеки классов с именем Domain, который содержит классы домена, на которые ссылаются все другие проекты. Я использую эти классы для передачи данных между слоями. На этот раз классы DAL остаются статическими, а BLL - обычными. Я переместил большую часть функциональности (запросов) в DAL. Теперь в DAL есть классы, такие как EmployeesDAL, DepartmentsDAL и общий класс, содержащий статические методы Select, Execute и ExecuteScalar.
Employee.cs
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Department department { get; set; }
public EmployeeType employeeType { get; set; }
}
EmployeesDAL
public static List<Employee> GetEmployees(int departmentid)
{
if (departmentid > 0)
{
string query = string.Format("SELECT * FROM EMPLOYEES WHERE department='{0}')", departmentid);
return GetCollection(OraDAL.Select(query));
}
return null;
}
public static Employee GetEmployee(int employeeid)
{
if (employeeid > 0)
{
string query = string.Format("SELECT * FROM PMS_EMPLOYEES WHERE Id='{0}'", employeeid);
return GetSingle(OraDAL.Select(query));
}
throw new Exception("Employee id not valid");
}
public static int AddEmployee(Employee employee)
{
if (employee != null)
{
string query = string.Format("INSERT INTO PMS_EMPLOYEES (name, department, employee_type) VALUES ('{0}','{1}','{2}')", employee.Name, employee.department.Id, employee.employeeType.Id);
return OraDAL.Execute(query);
}
throw new Exception("Values not valid");
}
private static List<Employee> GetCollection(DataTable table)
{
List<Employee> employees = null;
if (table != null)
{
if (table.Rows.Count > 0)
{
employees = new List<Employee>();
foreach (DataRow row in table.Rows)
{
employees.Add(ReadDataRow(row));
}
}
}
return employees;
}
private static Employee GetSingle(DataTable table)
{
if (table != null)
{
if (table.Rows.Count > 0)
{
DataRow row = table.Rows[0];
return ReadDataRow(row);
}
}
return null;
}
private static Employee ReadDataRow(DataRow row)
{
Employee employee = new Employee()
{
Id=int.Parse(row["ID"].ToString()),
Name=row["NAME"].ToString(),
employeeType=EmployeeTypesDAL.GetEmployeeType(int.Parse(row["EMPLOYEE_TYPE"].ToString())),
department=DepartmentsDAL.GetDepartment(int.Parse(row["DEPARTMENT"].ToString()))
};
return employee;
}
EmployeesBLL.cs
public class EmployeesBLL
{
public List<Employee> GetEmployees(int departmentid)
{
if (departmentid > 0)
{
return EmployeesDAL.GetEmployees(departmentid);
}
return null;
}
public int AddEmployee(Employee employee)
{
if (employee != null)
{
return EmployeesDAL.AddEmployee(employee);
}
throw new Exception("Employee cannot be null");
}
Пост становится длиннее, но я хочу, чтобы вы лучше поняли ситуацию. Я столкнулся с несколькими проблемами с новым дизайном и заставил меня задать этот вопрос, это правильный подход, чтобы делать вещи? Поскольку главная цель - избежать перемещения таблиц данных между различными слоями. Хотя этот новый способ кодирования логики слишком трудоемкий, но стоит ли он усилий. Бизнес-уровень становится слишком тонким, так как кажется, что единственная услуга, которую они предоставляют, - это вызов аналогичных методов из DAL. Нужен ли мне BLL в первую очередь? Если да, то какие могут быть ситуации, когда необходим отдельный BLL.
редактировать
Одной из проблем, которые я отметил с вышеупомянутым дизайном, является количество вызовов базы данных. Слишком много вызовов, поскольку при заполнении объекта все дочерние объекты также заполняются, что приводит к вызовам в базу данных. Например, заполнение объекта Employee приведет к заполнению экземпляра отдела. В большинстве случаев мне нужен только идентификатор отдела, а не вся информация об отделе.
2 ответа
Назначение частей 3-уровневой архитектуры заключается в следующем:
1. Пользовательский интерфейс
Позволяет пользователю взаимодействовать с моделью вашего домена
2. Доменная модель / Средний уровень
Содержит перевод вашего конкретного домена или бизнес-процесса в код. Служит представлением вашей конкретной бизнес-проблемы или среды. Этот уровень должен содержать все бизнес-правила и бизнес-объекты для вашего приложения.
3. Уровень доступа к данным
Позволяет сохранять объекты модели вашего домена и извлекать их из хранилища данных. Уровень доступа к данным просто берет данные из постоянного хранилища данных (т. Е. Базы данных) и превращает их в объекты модели домена, а также принимает объекты модели домена и сохраняет их в хранилище данных.
Из этих трех областей, на мой взгляд, наиболее важной областью на данный момент является модель домена / средний уровень. Это сердце и душа вашего приложения, и именно там приложение фактически решает конкретную бизнес-проблему и обеспечивает ценность организации, которая будет использовать ваше программное обеспечение. Цель двух других областей - просто представить вашу модель домена пользователям (пользовательский интерфейс) или разрешить ее извлечение или сохранение (уровень данных).
Следствием этого является то, что вы должны стремиться потратить как можно больше своих усилий, работая над моделью предметной области / промежуточным уровнем. Именно здесь вы решаете конкретные бизнес-проблемы своей организации, и именно здесь вы действительно можете повысить ценность своей компании или клиентов.
Применяя эти общие принципы к вашей конкретной ситуации, у меня есть следующие комментарии / предложения.
Вы вручную создаете свой слой доступа к данным и создаете вручную операторы SQL. В общем, это очень низкое значение использования вашего времени. Существует целый ряд инструментов сопоставления объектных отношений (ORM), таких как nHibernate или структура сущностей, которые позаботятся о низкоуровневой сантехнике, необходимой для загрузки объектов модели домена из хранилища данных и сохранения этих объектов обратно в хранилище. хранилище данных. Я думаю, что вы можете исследовать это. Я думаю, что решение с использованием одного из этих инструментов ORM будет лучшим решением, чем любой из предложенных вами вариантов.
Основываясь на опубликованном вами коде, ваша модель домена / средний уровень - это просто набор свойств, без бизнес-логики или бизнес-правил. Если это действительно так, то так и должно быть. Однако было бы полезно использовать ваше время, чтобы поговорить с заинтересованными сторонами и действительно приложить усилия к моделированию их процессов в модели предметной области, поскольку именно здесь вы можете лучше всего повысить ценность своей организации.
Я знаю, что это немного затянулось, но надеюсь, что это поможет вам.
Уровень презентации:
Вы уже знаете это. Это ваш пользовательский интерфейс. Он состоит из вида, модели представления и (контроллер / презентатор). Этот уровень представления имеет только логику, связанную с пользовательским интерфейсом (позиционирование, CSS-стилизация и т. Д.). Он не знает о реализации BLL (только интерфейс) и вообще не знает DAL (хранилище).
Бизнес уровень
Где находится логика вашего бизнеса. Все операции выбора, вычисления, проверки, вставки, удаления, обновления выполняются здесь. Он не знает о пользовательском интерфейсе, поэтому он может работать в любом пользовательском интерфейсе, будь то веб / настольное / мобильное приложение, с той же логикой busienss. Это означает, что на этом уровне не было использовано никакого очевидного кэширования / сеанса. Вместо этого вы можете обернуть его в некоторый интерфейс. Он не знает о хранилище, то есть вам нужен еще один слой для хранения, и это DAL.
Уровень доступа к данным
Этот уровень просто выполняет операции CRUD с хранилищем (плоский файл, база данных, веб-сервис, xml, json и т. Д.) И возвращает объектную модель. Это все. Он не знает ни пользовательского интерфейса, ни бизнес-правила. Цель этого уровня - облегчить его замену. Скажем, вам нужно заменить хранилище из базы данных в xml, это задача уровня, оставив другой слой нетронутым.
Отвечая на ваш вопрос
Надеюсь, вы уже знаете цель 3 уровня.
Теперь предположим, что вы используете 3 уровня с BLL в вашем случае:
- Любые изменения в хранилище сотрудников не влияют на BLL и пользовательский интерфейс
- Любые изменения в бизнес-логике (новый параметр поиска) влияют на DAL
- Ваш BLL не знает доступ к данным (контекст соединения / базы данных и т. Д.), Значит, ему вообще не нужно знать соединение
- Дополнительное очевидное отображение в BLL
Теперь предположим, что вы используете 2 уровня без BLL в вашем случае:
- Любые изменения в хранилище сотрудников не влияют на пользовательский интерфейс (без BLL)
- Любые изменения в бизнес-логике (новый параметр поиска) влияют на DAL
- Ваш пользовательский интерфейс знает DAL. Если вы разработали его как отдельный проект, ссылка будет добавлена и нарушит наслоение. Это открывает возможности для вашего интерфейса для прямого доступа к другому DAL.
Да, иметь BLL в вашем случае довольно бесполезно, поскольку вы получаете выгоду только для разделения вашего пользовательского интерфейса и DAL. Но в чем смысл 3 уровня, не так ли?
То есть, если вы используете хранимую процедуру, выберите. Что вам действительно нужно, так это общий шаблон репозитория в DAL, который очень поможет вам во время доступа к BLL.