Mastering Factory Pattern and Application Layer Design for SOA Systems
This article explains the appropriate use cases for the Factory pattern, details the responsibilities and characteristics of the application layer in SOA systems, and provides concrete C# code examples for services, DTOs, and data assemblers to illustrate clean separation of concerns and effective data transfer.
Factory Pattern (When to Use)
The Factory pattern is a common software design pattern, but it is not the core of Domain‑Driven Design. It should be used when object creation involves complex business rules between internal properties. For simple objects, constructors or simple factory methods suffice.
For example, when creating an Order, business rules may dictate that freight is 1% of the total amount and discounts are 75%. Encapsulating these rules in a Factory prevents the client from handling the logic directly.
Application Layer in SOA
Characteristics of the Application Layer
Coarse‑grained : The layer aggregates multiple domain objects into a single Data Transfer Object (DTO) to reduce remote calls and improve performance.
Transmissibility : DTOs carry data without business logic, isolating internal domain objects from external clients.
Encapsulation : The layer can expose services via Facade, Web Service, TCP/IP sockets, or MSMQ, keeping the model simple.
Coordination Role of the Application Layer
The application layer uses repositories to perform basic CRUD operations and delegates business logic to domain services. It should contain no business logic itself.
namespace Business.Service.ApplicationService
{
public class PersonService
{
private IPersonRepository personRepository = DataAccess.CreatePersonRepository();
public int AddPerson(Person person) { return personRepository.AddPerson(person); }
public int UpdatePerson(Person person) { return personRepository.UpdatePerson(person); }
public Person GetPerson(int personID) { return personRepository.GetPerson(personID); }
public IList GetList() { return personRepository.GetList(); }
}
public class OrderService
{
private IOrderRepository orderRepository = DataAccess.CreateOrderRepository();
public int AddOrder(Order order)
{
Account(order);
return orderRepository.AddOrder(order);
}
private void Account(Order order)
{
IPersonRepository personRepository = DataAccess.CreatePersonRepository();
Person person = personRepository.GetPerson(order.PersonID);
AccountManager accountManager = new AccountManager(person, order);
accountManager.Account();
}
public Order Payment(int orderID)
{
var order = orderRepository.GetOrder(orderID);
if (order != null)
{
PersonRepository personRepository = new PersonRepository();
var person = personRepository.GetPerson(order.PersonID);
PaymentManager paymentManager = new PaymentManager();
paymentManager.Payment(order, person);
orderRepository.UpdateOrder(order);
personRepository.UpdatePerson(person);
return order;
}
else
throw new Exception("Can not find order!");
}
// ... other CRUD methods omitted for brevity ...
}
}Data Transformation Process
DTOs act as carriers for remote communication. Converting between domain objects and DTOs can be complex, so a dedicated data assembler (OperationAssembler) is used.
namespace Business.Service.ApplicationService
{
public class OperationAssembler
{
public static OrderDTO GetOrderDTO(Order order, Person person)
{
OrderDTO dto = new OrderDTO();
if (person != null)
{
dto.EMail = person.EMail.GetString();
dto.Address = person.Address.GetString();
dto.Name = person.Name.GetString();
dto.PersonID = person.ID;
dto.Point = person.Point.GetInt();
dto.Telephone = person.Telephone.GetString();
}
if (order != null)
{
dto.PersonID = order.PersonID;
dto.Count = order.Count.GetInt();
dto.Delivery = order.Delivery.GetDateTime();
dto.Favorable = order.Favorable.GetDouble();
dto.Freightage = order.Freightage.GetDouble();
dto.OrderID = order.ID;
dto.OrderNumber = order.OrderNumber.GetString();
dto.Price = order.Price.GetDouble();
dto.TotalPrice = order.TotalPrice.GetDouble();
var items = order.OrderItem.ToList();
if (items.Count != 0)
{
var itemDtos = new List<OrderItemDTO>();
foreach (var item in items)
itemDtos.Add(GetOrderItemDTO(item));
dto.OrderItemList = itemDtos;
}
}
return dto;
}
public static OrderItemDTO GetOrderItemDTO(OrderItem item)
{
OrderItemDTO dto = new OrderItemDTO();
dto.Count = item.Count.GetInt();
dto.Goods = item.Goods.GetString();
dto.OrderID = item.OrderID;
dto.OrderItemID = item.ID;
dto.Price = item.Price.GetDouble();
return dto;
}
public static void SetOrder(OrderDTO dto, out Person person, out Order order)
{
person = new Person();
person.EntityKey = new System.Data.EntityKey("BusinessContext.Person","ID",dto.PersonID);
person.Address = dto.Address;
person.EMail = dto.EMail;
person.ID = dto.PersonID;
person.Name = dto.Name;
person.Point = dto.Point;
person.Telephone = dto.Telephone;
order = new Order();
order.EntityKey = new System.Data.EntityKey("BusinessContext.Order","ID",dto.OrderID);
order.Count = dto.Count;
if (dto.Delivery.Year != 0001 && dto.Delivery.Year != 9999)
order.Delivery = dto.Delivery;
order.Favorable = dto.Favorable;
order.Freightage = dto.Freightage;
order.ID = dto.OrderID;
order.OrderNumber = dto.OrderNumber;
order.PersonID = dto.PersonID;
order.Price = dto.Price;
order.TotalPrice = dto.TotalPrice;
if (dto.OrderItemList != null && dto.OrderItemList.Count != 0)
foreach (var itemDto in dto.OrderItemList)
order.OrderItem.Add(GetOrderItem(itemDto));
}
public static OrderItem GetOrderItem(OrderItemDTO dto)
{
OrderItem item = new OrderItem();
item.EntityKey = new System.Data.EntityKey("BusinessContext.OrderItem","ID",dto.OrderItemID);
item.Count = dto.Count;
item.Goods = dto.Goods;
item.ID = dto.OrderItemID;
item.OrderID = dto.OrderID;
item.Price = dto.Price;
return item;
}
}
}
// DTO definitions
namespace Business.TransferObject
{
[DataContract]
public class OrderItemDTO
{
[DataMember] public int OrderItemID { get; set; }
// other members omitted for brevity
}
[DataContract]
public class OrderDTO
{
[DataMember] public int PersonID { get; set; }
// other members omitted for brevity
}
}Publishing Application Layer Services
Application services are exposed via remote methods (e.g., WCF). A Facade aggregates the few remote service classes.
namespace Business.Service.ApplicationService
{
[ServiceContract]
public interface IOperationService
{
[OperationContract] int AddOrder(ref OrderDTO orderDTO);
[OperationContract] int DeleteOrder(OrderDTO orderDTO);
[OperationContract] int UpdateOrder(ref OrderDTO orderDTO);
[OperationContract] IList GetOrderByPerson(int personID);
[OperationContract] OrderDTO GetOrder(int orderID);
// ... other operations omitted ...
}
public class OperationService : IOperationService
{
[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
public int AddOrder(ref OrderDTO orderDTO)
{
OrderService svc = new OrderService();
Order order = GetOrder(orderDTO);
int n = svc.AddOrder(order);
orderDTO = OperationAssembler.GetOrderDTO(order, null);
return n;
}
// ... other method implementations omitted for brevity ...
}
}Overall System Architecture
DDD‑Based Architecture
The repository layer maps persistence data to domain objects, domain services contain business logic, the application layer coordinates repositories and domain services, and a data assembler converts domain objects to DTOs for remote transmission.
Service‑Oriented Architecture
SOA separates the presentation layer from the application service layer via remote communication, allowing independent development of functional modules that can be combined through services, a precursor to cloud computing.
Conclusion
The article shares practical experiences in building a DDD‑driven SOA system, emphasizing clean separation of concerns, proper use of the Factory pattern, and the importance of DTOs and remote services for scalable architecture.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITFLY8 Architecture Home
ITFLY8 Architecture Home - focused on architecture knowledge sharing and exchange, covering project management and product design. Includes large-scale distributed website architecture (high performance, high availability, caching, message queues...), design patterns, architecture patterns, big data, project management (SCRUM, PMP, Prince2), product design, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
