I have the following DB model:
**Person table**
ID | Name | StateId
------------------------------
1 Joe 1
2 Peter 1
3 John 2
**State table**
ID | Desc
------------------------------
1 Working
2 Vacation
and domain model would be (simplified):
public class Person
{
public int Id { get; }
public string Name { get; set; }
public State State { get; set; }
}
public class State
{
private int id;
public string Name { get; set; }
}
The state might be used in the domain logic e.g.:
if(person.State == State.Working)
// some logic
So from my understanding, the State acts like a value object which is used for domain logic checks. But it also needs to be present in the DB model to represent a clean ERM.
So state might be extended to:
public class State
{
private int id;
public string Name { get; set; }
public static State New {get {return new State([hardCodedIdHere?], [hardCodeNameHere?]);}}
}
But using this approach the name of the state would be hardcoded into the domain.
Do you know what I mean? Is there a standard approach for such a thing? From my point of view what I am trying to do is using an object (which is persisted from the ERM design perspective) as a sort of value object within my domain. What do you think?
Question update:
Probably my question wasn't clear enough.
What I need to know is, how I would use an entity (like the State example) that is stored in a database within my domain logic. To avoid things like:
if(person.State.Id == State.Working.Id)
// some logic
or
if(person.State.Id == WORKING_ID)
// some logic
Your proposed structure seems fine. (Terminology digression: since State has an ID, it's not a Value Object, but rather an Entity.)
Enums are a code smell, so don't attempt to go that route. It's much more object-oriented to move the behavior into the State object using the State pattern.
Instead of having to write
if (person.State == State.Working)
// do something...
all over your code, this would allow you to write
person.State.DoSomething();
That's much cleaner, and will allow you to add new States if need be.
A previous question of mine unearthed some useful links that I suspect are pertinent to your question, in particular Jimmy Bogard's discussions of Enumeration Classes.
It's a common practice to include an 'Unknown' element with value 0 in an enum. You can do this and use it for the New state if you really want to.
But what you are describing is business logic... setting a state after creating a new object should then happen in the business logic layer, not inside the class itself.
You want to create a factory method that will instantiate the appropriate state class needed, based on the value stored.
something like
public static State GetStateByID( StateEnum value)
{
if(value.Invalid)
throw new Exception();
switch(value)
case State.Working
return new WorkingState();
case State.somethingelse
return new somethingelseState();
case State.something
return new somethingState();
case State.whatever
return new whateverState();
}
When using enums always try to use 0 as Invalid. Under the hood an enum is a value type, and an unassigned int is always 0.
It is common to use a factory, such as this, in conjunction with the state pattern.
So when you read your stored integer value from the database you can cast the int to the enum and call the factory with it to get the appropriate State object.
I personally think it's a mistake to program against IDs. Instead, I would amend your table to the following:
**State table**
ID | Desc | IsWorking | IsVacation
-----------------------------------------------------------
1 Working True False
2 Vacation False True
I would then use these attributes to make business decisions on such as:
public void MakeDecisionOnState(State state)
{
if (state.IsVacation)
DoSomething();
if (state.IsWorking)
DoSomethingElse();
}
Or by being even more clever, use the factory pattern to create the correct instance based on these attributes:
public abstract class State
{
public Guid Id { get; set; }
public string Description { get; set; }
public abstract void DoSomething();
}
public class WorkingState : State
{
public override void DoSomething()
{
//Do something specific for the working state
}
}
public class VacationState : State
{
public override void DoSomething()
{
//Do something specific for the vacation state
}
}
public class StateFactory
{
public static State CreateState(IDataRecord record)
{
if (record.GetBoolean(2))
return new WorkingState { Id = record.GetGuid(0), Description = record.GetString(1) };
if (record.GetBoolean(3))
return new VacationState { Id = record.GetGuid(0), Description = record.GetString(1) };
throw new Exception("Data is screwed");
}
}
Now you've eliminated the if/switch statement, and your code could simply be:
state.DoSomething();
The reason why I do this is that often these types of entities can be configured by the customer, i.e. they may not want to have some of the states active in the system, or they may wish to term them something else. By programming against the attributes the customer can delete / edit the records as they please and even if that process generates new ID's it doesn't affect the system, they just need to set the attributes.
In my opion the domain layer has to be seperated from the DB model / ERM design. I had trouble understanding your final suggestion for the State class. IMHO this is not a good thing for establishing a common language which is one of the main purposes of DDD.
I would go for a simpler design. The state belongs to the Person class. I would include it in the class.
public class Person
{
public int Id { get; }
public string Name { get; set; }
public PersonState State { get; set; }
}
The state itself seems to have defined values (I assume a person is an employee in your context) which don't change very often. So I would model it as enum and treat it as a data type.
enum Days {Working, Vacation};
This is a simple to understand design in my opinion. The mapping to the ERM design belongs IMHO in the persistence layer. There the enum has to be mapped to the key of the state table. This could be done using an aspect to keep the original domain model clean.
Related
Is it a good practice to use multiple DTO's for same entity in different API endpoints. For example:
I have a api endpoint which accpets the following Dto:
public class AddressDto
{
public string City { get; set; }
public string Country { get; set; }
public string Contact { get; set; }
public string Street1 { get; set; }
public string Street2 { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
And now there is second Api which accepts the same dto but in that api call I'm using only Streer1, Street2, Contact all other are ignored.
Should I make another DTO for second api endpoint like:
public class AddressDtoForSecondAPI
{
public string Contact { get; set; }
public string Street1 { get; set; }
public string Street2 { get; set; }
}
In short, yes it is acceptable.
However, as you can see in the comments and the other answer, not everyone agrees here. So let me explain my answer.
Argument 1 - Misleading the consumer
And now there is second Api which accepts the same dto but in that api call I'm using only Streer1, Street2, Contact all other are ignored.
The issue here is one of making your intentions clear. If you allow a consumer to send you a fully fleshed AddressDTO, but then only use a subset of properties, then you're misleading your consumer. You've made them think that the other properties are relevant.
This is effectively the same as:
public int AddNumbersTogether(int a, int b, int c, int d)
{
return a + c + d; //we ignore b
}
There is no reason for b to exist. Anyone who uses this method is going to be scratching their head when AddNumbersTogether(1,2,3,4) returns a value of 8. The syntax contradicts the behavior.
Yes, it's easier to omit an unused method parameter than it is to develop a second DTO. But you need to be consistent here and stick to the same principle: not misleading the consumer.
Argument 2 - A DTO is not an entity
Your consumer's interaction with your API(s) needs to happen without the consumer knowing anything about the structure of your database records.
This is why you're using a DTO and not your entity class to begin with! You're providing a logical separation between taking an action and storing the data of that action.
The consumer doesn't care where the data is stored. Regardless of whether you store the street in the same table as the address, or a diferent table (or database) altogether, does not matter in scope of the consumer calling an API method.
Argument 3 - Countering S.Akbari
What about inheritance and/or interface segregation principle in SOLID? – S.Akbari
These are not valid arguments for this particular case.
Inheritance is a flawed approach. Yes, you can technically get away with doing something like AddressDto : AddressDtoForSecondAPI in the posted example code, but this is a massive code smell.
What happens when a third DTO is needed, e.g. one where only zip codes and city names are used? You can't have AddressDto inherit from multiple sources, and there is no logical overlap between AddressDtoForSecondAPI and the newly created AddressDtoForThirdAPI.
Interfaces are not the solution here. Yes, you could technically created an IAddressDtoForSecondAPI and IAddressDtoForThirdAPI interface with the appropriate fields, and then do something like AddressDto : IAddressDtoForSecondAPI, IAddressDtoForThirdAPI. However, this is the same massive code smell again.
What happens if the second and third variation have a few shared properties, and a few individual ones? If you apply interface segregation, then the overlapping properties need to be abstracted in an interface by themselves.
If then a fourth variation presents itself, which has some properties in common with the second variation, some with the third variation, some with both the second AND third variation, and some individual properties, then you're going to need to create even more interfaces!
Given enough variations of the same entity and repeatedly applying the interface segregation principle; you're going to end up with an interface for every property of the entity; which requires a ridiculous amount of boilerplating. You'll end up with something like:
public class AddressDto : IAddressCity, IAddressCountry, IAddressContact, IAddressStreet1, IAddressStreet2, IAddressState, IAddressZip
{
public string City { get; set; }
public string Country { get; set; }
public string Contact { get; set; }
public string Street1 { get; set; }
public string Street2 { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
Imagine having to do this for all classes; since the same principle would apply to every DTO that is being used by the API.
Argument 4 - DRY does not apply here
I sort of get why you're apprehensive of creating two classes. Most likely, there's a DRY/WET error flag being raised in your mind.
Avoiding WET is a good reflex to have; but you can't always listen to it. Because if you were to really avoid duplication, then you should effectively also not create separate entity and DTO classes, as they are usually copy/pastes of each other.
DRY is not an absolute. Taking the entity/DTO example, there is a balance of considerations here:
Do you want avoid repetition at all costs? (= DRY)
Do you want to separate your DAL from your API logic? (= separation of concerns)
In this case, the latter generally wins out.
The same argument applies in your case. The argument against following DRY (which is the arguments I just listed) far outweighs the benefits of following DRY in this scenario.
I need to implement 1..* and 1..1 relationships in a store scenario application.(Classes: Member, Order, OrderLine, Product, Program, User) How do i go about a 1 user only having 1 Order that can have many OrderLines (preferably using a List structure?
This is my User class:
namespace ConsoleApplication1
{
public class User
{
private string ffName;
private string llName;
private int id = 0;
//Constructor
public User(string firstName, string lastName)
{
fName = firstName;
lName = lastName;
}
public User() {}
//Overrides
public override bool Equals(object obj)
{
return obj.ToString() == this.ToString();
}
public override int GetHashCode()
{
return this.ToString().GetHashCode();
}
public override string ToString()
{
string myUser;
myUser = string.Format("First Name: {0}; Last Name: {1}", fName, lName);
return myUser;
}
// Properties
public string fName
{
get
{
return ffName;
}
set
{
ffName = value;
}
}
public string lName
{
get
{
return llName;
}
set
{
llName = value;
}
}
}
}
You can have an Order class and an OrderLine class. The Order class will have a List of OrderLines and the User class can have a Order member.
Something like:
public class User
{
private string ffName;
private string llName;
private int id = 0;
private Order order = null;
//Constructor
public User(string firstName, string lastName)
{
fName = firstName;
lName = lastName;
}
...
}
public class Order
{
List<OrderLine> orderLines = null;
}
public class OrderLine
{
}
You have to implement the Order and the OrderLine class as:
class OrderLine
{
//some code
}
class Order
{
List<OrderLine> lstOrderLine;
//some code
}
Then add the Order class to your user class.
Edit: Removed snarkyness and attitude :)
First you need an order (hint you are going to need a class for that). Now the order needs to be attched to a user. So add a field of type User. That takes care of one order one user. (Note that a user can make more than one order)
So now you order is missing lines. Add another member variable that is a list of line types. Now in your order you need to add methods to add, remove and query order lines.
Edit: The question was raised what was meant by "add a field". Add a field means add a property or private member. When you are doing this you are doing the technical term of composition. Composition is commonly explained as a "has a" relationship. So an order "has a user" and "has a list of order lines"
Class User()
{
public string firstName { get; set; }
public string lastName {get; set; }
public int id { get; set;}
}
Class OrderLine()
{
}
Class Order()
{
private List<OrderLine> orderLines;
public User submitter { get; set;}
public Order()
{
orderLines = new List<OrderLine>();
}
public void AddOrderLine(OrderLine newOrderLine)
{
this.orderLines.Add(newOrderLine);
}
public IList<OrderLine> GetOrderLines()
{
return this.orderLines;
}
}
Example
User customer1 = new User();
// Initialize customer1 values...
Order someOrder = new Order();
someOrder.submitter = customer1;
someOrder.AddOrderLine(new OrderLine());
EDIT: Changed Member class to User class
Your most recent comment cleared up your question:
Its not hard to create each one i just dont understand how to get the relationship to work with 1..* or 1..1. If i create an Order i can always create another order
So, let's talk about the types of relationships.
Relationship types
Relationship types don't talk about absolute numbers of entities in the system. They just talk about numbers of entities in relation to other entities.
1:1 Relationship
This means that the two entity types must exist in pairs. If one entity of type A exists, then only one entity of type B can exist. For example, your User and Order. An order can't exist without a User, and a User can only have one Order. This doesn't mean there is only one User - there could be 42 users. This just means that if an Order exists, a User must also exist, and that the User can only have one Order.
There is a strict and less strict version of this. Technically, I just described something like a 1:{0 or 1} relationship. In a real 1:1 relationship you would require that the Order exists if the User exists. Neither could exist if the other didn't exist. However this constraint is usually relaxed when talking about relational databases (but only in one direction - in this case you still can't have an Order without a User).
You can model this relationship with code like this:
public class User
{
public Order Order { get; set; }
}
public class Order
{
// You could put a reference here back to the User if you want...
}
Note that it is a bit weird to only support only one Order for a User. It makes more sense to make it 1:*. But if that is a requirement of your assignment, then this is how you'd model it.
1:* Relationship
This is similar to the 1:1 relationship. But it relaxes some of the restrictions so that if an entity of type A exists, then any number (including zero) of type B can exist. The example is the Order and OrderLine. Again, there is no restriction on how many of either entity type exist. There could be 57 orders in the system. You just can't have an OrderLine without an Order, and there could be multiple OrderLines per Order.
You can model this relationship with code like this:
public class Order
{
public List<OrderLine> OrderLines { get; set; }
}
public class OrderLine
{
// You could put a reference here back to the Order if you want...
}
Enforcing relational concepts in code
I can't speak for your assignment, so make sure you back up what I am saying here against what your assignment requires.
You should not try to enforce basic relational concepts like these in code. The database is better at it, has better (declarative) language to describe the relationships, and is going to be your ultimate source of data for the system.
Instead, you should just do a soft model that follows the relationships (as the code samples above do), and let the database do the real policing of those constraints.
Examples:
You should not try to restrict construction of Order types in code, and you shouldn't require a User to exist to construct an Order (as code entities).
You should not require an Order to exist to create an OrderLine (as code entities).
Trying to put these sorts of restrictions in code buys you nothing. When you persist the entities to the database, the database will ensure these relationships for you (assuming you've set it up correctly, which you will learn to do). Your error will be caught, and you'll learn habits that avoid these types of errors very quickly.
Trying to put these sorts of restrictions in code hurts you. It will be harder to write your program, and it will be harder to write unit tests for your code.
For example, consider an algorithm or test that compares OrderLine values. Maybe you want it to compare to a hypothetical OrderLine. If you had relational restrictions in place in your code, you'd also have to create a hypothetical Order and User. Would you also compare the hypothetical User and Order to the real ones? What if your algorithm shouldn't care what User or Order it originated from? If you're not going to compare them, why bother creating them to begin with?
So: Don't worry about it. Softly model your relationships so that it is easy to navigate between your objects, and let the database do your strict relationship validations for you.
Is there a better way to limit access to the Occupation and Employer properties?
This class is simply designed to collect a person's (potential customer's) employment information. EmploymentStatus can be Employed, SelfEmployed, Unemployed, Retired, etc...
I only want users of this class to be able to set Employer and Occupation if the person is indeed employed.
public class EmploymentInformation
{
private const string _EmploymentStatusNotEmployedMessage = "Employment status is not set to employed";
private string _occupation;
private Company _employer;
/// <summary>The person's employment status<example>Employed</example></summary>
public EmploymentStatus EmploymentStatus { get; set; }
/// <summary>The person's occupation<example>Web Developer</example></summary>
public string Occupation
{
get
{
if (IsEmployed)
{
return _occupation;
}
throw new ApplicationException(_EmploymentStatusNotEmployedMessage);
}
set
{
if (IsEmployed)
{
_occupation = value;
}
throw new ApplicationException(_EmploymentStatusNotEmployedMessage);
}
}
/// <summary>The person's employer</summary>
public Company Employer
{
get
{
if (IsEmployed)
{
return _employer;
}
throw new ApplicationException(_EmploymentStatusNotEmployedMessage);
}
set
{
if (IsEmployed)
{
_employer = value;
}
throw new ApplicationException(_EmploymentStatusNotEmployedMessage);
}
}
private bool IsEmployed
{
get
{
return EmploymentStatus == EmploymentStatus.Employed
|| EmploymentStatus == EmploymentStatus.SelfEmployed;
}
}
/// <summary>
/// Constructor for EmploymentInformation
/// </summary>
/// <param name="employmentStatus">The person's employment status</param>
public EmploymentInformation(EmploymentStatus employmentStatus)
{
EmploymentStatus = employmentStatus;
}
}
Anything wrong with simply returning null if the value is not set? That's fairly common practice. If Employer does not exist it's value is null. Why it's null may not be relevant. In addition force the employment status to be set within the ctor of the class itself.
Forcing developers to set properties in a particular order is a dangerous design: it makes the interface misleading and encourages mistakes.
Instead, consider making EmploymentInformation objects immutable:
// Constructor for retired / unemployed people
public class EmploymentInformation(EmploymentStatus status) {}
// Constructor for self-employed people - we know their status
public class EmploymentInformation(string occupation) {}
// Constructor for people employed by others - we know their status
public class EmploymentInformation(string occupation, Company employer) {}
public bool IsEmployed { get; }
public string Occupation { get; }
public Company Employer { get; }
Firstly, why is it possible to construct an object of EmploymentInformation if there is no Employer?
As far as possible, you should not allow an object to be constructed in an invalid state. You can express these constraints in the constructor of your object either using Guard Clauses or Code Contracts.
public class EmploymentInformation
{
public EmoloymentInformation(Employer employerInstance)
{
if(employerInstance == null)
throw new ArgumentNullException();
}
Secondly, you can use the Null Object pattern so that you don't have to throw exceptions. Just create appropriate class for EmptyEmployer and return them as shown below.
public Company Employer
{
get
{
return IsEmployed ? _employer : Employer.Empty;
// Employer.Empty is static property which return an instance of EmptyEmployer just like string.Empty.
}
A new answer:
Given that the object is strictly to hold data regarding the users CURRENT employement status, it's still wrong.
As #Jeff Sternal said you shouldn't force dev's to assign parameters based on a particular order. In the event the object needs to be serialized/deserialized you could end up with a lot of errors.
Instead you should provide a validation function. Something like bool IsValid(); When that method is called perform the business logic validation to ensure that the object is in an acceptable state. You could have it simply return a false if not, throw an exception (please don't), or have it send a status code back as to why the object is not currently valid.
Typically you throw data into an object THEN you validate the object is good prior to persistence. The above is just one way of doing this. Others include having a Business Logic library which separates the logic completely from the data classes (personally, I never understood why you'd do this, but a lot of people swear by it.).
I've not had any experience in this, but somewhere I would consider looking is in Code Contracts.
Have a look at these links:
http://social.msdn.microsoft.com/Forums/en/codecontracts/thread/1ca2d371-4b85-479d-9e00-64c84e372f02
http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx
You can decorate your properties with "requirements" suitable to your application. Looks cool to use, and it appears to be half integrated into the IDE.
This looks wrong from a logic perspective.
The object is called "EmploymentInformation" and has a property called "EmploymentStatus"
This seems to either allow for situations where the employmentinformation deals with active or term'd employees; or, to allow for employment history.
If either of those are true, then it seems to me that you can have an Occupation but have an EmploymentStatus of something like "NotEmployed" for whatever reason.
After all, let's see the record is initially created where the EmploymentStatus is employed. Then later the status is changed to "NotEmployed" The next time you go to load the object you are going to lose data.
If you were being strict you might argue that an unemployed person does not have an occupation or employer, so a person object should not have these properties. That leads to something like this.
class Person
{
public EmploymentStatus EmploymentStatus { get; set; }
}
class EmployedPerson : Person
{
public string Occupation { get; set; }
public Company Employer { get; set; }
}
However in practice this unforgiving object model will be cumbersome to work with as you will need to know whether or not a person is employed before you can instantiate an object. It will also be difficult to change between being employed and unemployed as you will have to create a new object and copy everything across.
The clinical distinction isn't worth it. I think it's just as correct and in fact more logical to ask an unemployed person who their employer is and for them to reply with "I haven't got one" rather than be unable to ask the question in the first place.
For me, this would be a more flexible person class.
class Person
{
public Person()
{
this.EmploymentStatus = EmploymentStatus.Unemployed;
}
public void Hire(Company employer, string occupation)
{
this.Occupation = occupation;
this.Employer = employer;
this.EmploymentStatus = EmploymentStatus.Employed;
}
public void Fire()
{
this.Occupation = null;
this.Employer = null;
this.EmploymentStatus = EmploymentStatus.Unemployed;
}
public EmploymentStatus EmploymentStatus { get; private set; }
public string Occupation { get; private set; }
public Company Employer { get; private set; }
}
I have a persistence ignorant domain model that uses abstract repositories to load domain objects.
The concrete implementation of my repositories (the data access layer (DAL)) uses entity framework to fetch data from a sql server database.
The database has length constraints on a lot of its varchar columns.
Now imagine that I have the following domain class:
public class Case
{
public Case(int id, string text)
{
this.Id = id;
this.Text = text;
}
public int Id { get; private set; }
public string Text { get; set; }
}
And an abstract repository defined as follows:
public abstract class CaseRepository
{
public abstract void CreateCase(Case item);
public abstract Case GetCaseById(int id);
}
The [text] column of the table in sqlserver is defined as nvarchar(100)
Now I know that I mentioned that my domain class (Case) was persistence ignorant, nevertheless I feel that it is wrong that it allows
for values of the text parameter that cannot ultimately be saved by my concrete repository implementation because the entity framework
will throw an exception when assigning the text property to the entity framework generated class when it is longer than 100 characters.
So I have decided that I wish to check this constraint in the domain model, because this allows me to check data validity before attempting to
pass it on to the DAL, and thus making error reporting more centric to the domain object. I guess you could argue that I could just check the
constraint in my constructor and in the property setter, but since I have hundreds of classes that all have similar constraints I wanted a
more generic way to solve the problem
Now, the thing that I've come up with is a class called ConstrainedString, defined as follows:
public abstract class ConstrainedString
{
private string textValue;
public ConstrainedString(uint maxLength, string textValue)
{
if (textValue == null) throw new ArgumentNullException("textValue");
if (textValue.Length > maxLength)
throw new ArgumentException("textValue may not be longer than maxLength", "textValue");
this.textValue = textValue;
this.MaxLength = maxLength;
}
public uint MaxLength { get; private set; }
public string Value
{
get
{
return this.textValue;
}
set
{
if (value == null)
throw new ArgumentNullException("value");
if (value.Length > this.MaxLength) throw new ArgumentException("value cannot be longer than MaxLength", "value");
this.textValue = value;
}
}
}
Furthermore I have an implementation of ConstrainedString called String100 :
public class String100 : ConstrainedString
{
public String100(string textValue) : base(100, textValue) { }
}
Thus leading to a different implementation of Case that would look like this:
public class Case
{
public Case(int id, String100 text)
{
this.Id = id;
this.Text = text;
}
public int Id { get; private set; }
public String100 Text { get; set; }
}
Now, my question is; Am I overlooking some built-in classes or some other approach that I could use instead? Or is this a reasonable approach?
Any comments and suggestions are most welcome.
Thank you in advance
I believe your validation should reside in your domain model. The constraints on your fields directly represent some business logic. Ultimately you have to validate before you persist anyway.
I think this depends on many factors (as well as some personal preferences). Sometimes the constraint should form part of the domain object - for example with social security numbers/passport numbers... - these normally have a fixed length and cannot vary as a domain rule - not a data persistence rule (although you might constrain the db as well).
Some prefer to not have these sort of checks in their domain model and instead have something like a validation attribute on the property that can be inspected and executed external from the domain object by a seperate validator.
The issue you might have with your method (although not difficult to get around) is getting any ORM/Mapper - if you're using one - to know how to map a string to/from the db to your ConstrainedString.
The ConstrainedString might not get around the issue of the domain object having extra info about the constraint as it might need to construct the ConstrainedString
If you change the constraints of a Case, it makes sense that you'd have to make a new one - you've changed the contract, and old code will no longer know if it's meeting the requirements or not.
Instead of worrying about what your repository will or will not allow, define what you will allow in your class, and make sure that you find a way to work with any repository that you change to in the future. You own your API - your dependencies do not.
I received the following email today from a co-worker. My question is this accurate. Nesting Business Objects is bad practice? Can anyone shine in on this?
Nested Objects
When any variable is created within C# it takes up a piece of memory on the Web Server. Since we will have many tools running on the same server, it is even more important to ensure we are not creating objects if we don't plan on using them.
Using the second employee object above as an example… If we also needed to know the employees Supervisor ID.. (and that was all the tool was populating and using) we would want to ensure the Employee class contains the appropriate information, along with taking into consideration Memory and Processes in the tool.
We would add the 'supervisorId' string variable to the Employee class, and add the appropriate Getters and Setters.
On the flip side, we would want to shy away from nesting another object within the employee object. Such as:
public class Employee {
private string firstName;
private string lastName;
private string empId;
private Employee supervisor;
public string FirstName {
get { return firstName; }
set { firstName = value; }
}
public string LastName {
get { return lastName; }
set { lastName = value; }
}
public string EmpId {
get { return empId; }
set { empId = value; }
}
public Employee Supervisor{
get { return supervisor; }
set { supervisor = value; }
}
}
In this case we may not always use the values within the 'Supervisor' instance of the Employee object, but the variables are created in memory. This can have a potentially catastrophic effect on performance.
There are 'some' cases where nesting of objects is necessary:
Example: (Category :: Question) Where each category could have an array list of questions assigned to it.
The short answer to your general question of
Is it bad to nest business objects?
is no.
The long answer is that is sounds like your team is suffering from premature optimization. You need to design your business objects to mirror your business domain. All the behaviors in your business domain should be exemplified in your business layer. Once you've achieved that goal, you can then do performance testing. Actually measure what parts of your system is too slow, and then optimize those parts. Don't get caught up in preoptimizing your business logic before you've even had a chance to get it laid out.
Design and implement, then performance test and then optimize when you find unacceptable slowness.
My opinion is that you should nest only when you'll be routinely calling a method on the nested object.
If all you will do with the nested object is to get some properties of it, then you shouldn't have it nested and should store the properties directly.
It appears from your code sample that you're setting the supervisor Employee object externally (i.e. through the property setter), so I think this design is OK. If you were automatically instantiating the supervisor object (by, say, hitting the database) every time you created the "outer" Employee object, you would have a potential problem.
I believe the following Business Object (Data Transfer Objects) sparked the email:
/// <summary>
/// Manufacturer Data Transfer Object
/// </summary>
public class MfgBO {
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
}
}
public class TypeBO {
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
}
public class ModelBO {
#region Private Variables
private int mmtId = -1;
private int id = -1;
private string name = String.Empty;
private bool active = false;
private MfgBO mfg = new MfgBO();
private TypeBO type = new TypeBO();
#endregion
// Getter and setters below
Looking at this the ModelBO holds the MfgBO and a TypeBO because a model cannot be complete without the info. What he is recommending is in the ModelBO instead of having MfgBO or TypeBO, we should have a variable of int MakeID, string MakeName, int DeviceTypeId, string DeviceTypeName, etc, basically retyping fields that already exist in MfgBO and TypeBO objects.
To my limited OOP knowledge it makes more sense to use the MfgBO and TypeBO. Which is better way for my own personal knowledge? Is having the MfgBO and TypeBO in MakeBO will actually use more memory and "potentially crash the server"?
You could create the object only if you explicitly access it.
public BusinessObject Item
{
get
{
if (_Item == null)
_Item = new BusinessObject();
return _Item;
}
}
private BusinessObject _Item;