I use CouchDB and I would like all my users to have unique emails. I would expect that the database return a status 400 (bad request) when I try to duplicate an email.
But since there is no way to define constraints in CouchDB, I should implement it myself, my question is:
In which layer of my application should this rule stand?
(1) Domain objects layer
I don't really know how to implement it in this layer
(2) Interactor layer
This constraint could be implemented in interactors here since there is the place where business rules stand. But if there is multiple rules for single document it could add unnecessary complexity...
function createUser(userData) {
let email = userData.email;
let exist = await userDB.userExist(email);
if(exist) {
// return status 400
} else {
// create user
}
}
(3) Database gateway layer
The constraint can also be implemented in the database gateway layer. Usually we'll have a gateway for each specific entity. But does that means that external services adapters contains a bit of business logic?
class userDB() {
constructor(opts) {
this.db = opts.db.connect();
}
async userExist(email) {
return await this.db.fetchByView('email', email);
}
async create(email) {
let exist = await this.userExist(data.email);
if(exist) {
// throw error
} else {
// create the user
}
}
}
Unique email address is a very old DDD topic. It relates to the set validation.
The simplest (and from my point of view is also the best) is to place a constraint at the database level.
From what I know, the only whay to create an unique constraint in CouchDB is to use the _id field so you can use this solution. This idea is to put the email in the _id field.
let exist = await this.userExist(data.email);
if(exist) { // throw
error } else { // create the user }
This method is not safe for concurrent updates. Two users can be created at the same time. Imagine that for both the requests the this.userExist(data.email) is returning false.
I am not a CouchDB expert but from pure Clean Architecture perspective ensuring unique email addresses is a business rule which should be implemented in an interactor.
With this approach ur business rule would remain intact even if u would once decide to replace the detail CouchDB with some other detail - another storage system.
Related
In the screenshot below, I have got an utterance conflict, which is obvious because I am using similar patterns of samples in both the utterances.
My question is, the skill I am developing requires similar kind of patterns in multiple utterances and I cannot force users to say something like “Yes I want to continue”, or “I want to store…”, something like this.
In such a scenario what is the best practice to avoid utterance conflicts and that too having the multiple similar patterns?
I can use a single utterance and based on what a user says, I can decide what to do.
Here is an example of what I have in my mind:
User says something against {note}
In the skill I check this:
if(this$inputs.note.value === "no") {
// auto route to stop intent
} else if(this$inputs.note.value === "yes") {
// stays inside the same intent
} else {
// does the database stuff and saves the value.
// then asks the user whether he wants to continue
}
The above loop continues until the user says “no”.
But is this the right way to do it? If not, what is the best practice?
Please suggest.
The issue is really that for those two intents you have slots with no context around them. I'm also assuming you're using these slots as catch-all slots meaning you want to capture everything the person says.
From experience: this is very difficult/annoying to implement and will not result in a good user experience.
For the HaveMoreNotesIntent what you want to do is have a separate YesIntent and NoIntent and then route the user to the correct function/intent based on the intent history (aka context). You'll have to just enable this in your config file.
YesIntent() {
console.log(this.$user.$context.prev[0].request.intent);
// Check if last intent was either of the following
if (
['TutorialState.TutorialStartIntent', 'TutorialLearnIntent'].includes(
this.$user.$context.prev[0].request.intent
)
) {
return this.toStateIntent('TutorialState', 'TutorialTrainIntent');
} else {
return this.toStateIntent('TutorialState', 'TutorialLearnIntent');
}
}
OR if you are inside a state you can have yes and no intents inside that state that will only work in that state.
ISPBuyState: {
async _buySpecificPack() {
console.log('_buySpecificPack');
this.$speech.addText(
'Right now I have a "sports expansion pack". Would you like to hear more about it?'
);
return this.ask(this.$speech);
},
async YesIntent() {
console.log('ISPBuyState.YesIntent');
this.$session.$data.productReferenceName = 'sports';
return this.toStatelessIntent('buy_intent');
},
async NoIntent() {
console.log('ISPBuyState.NoIntent');
return this.toStatelessIntent('LAUNCH');
},
async CancelIntent() {
console.log('ISPBuyState.CancelIntent()');
return this.toStatelessIntent('LAUNCH');
}
}
I hope this helps!
Following the good practices of DDD, Aggregate and Aggregate Root. I have the following scenario:
User (Aggregate Root)
A collection of UserEmail (inside User)
Imagining that I have registered a User with 10 Emails, what would be the most correct and perfomable way of updating one of these emails?
Method 1
static void UpdateEmailForExistingUserMethod1()
{
var userId = new Guid("f0cd6e3e-b95b-4dab-bb0b-7e6c6e1b0855");
var emailId = new Guid("804aff75-8e48-4f53-b55d-8d3ca76a2df9");
using(var repository = new UserRepository())
{
// I'm going to return the user with all their emails?
// I will not have performance problems for bringing all emails from this user?
var user = repository.GetUserById(userId);
if (user == null)
{
Console.WriteLine("User not found");
return;
}
// Updating Email in Aggregate Root
user.UpdateEmail(emailId, "updated1#email.com");
// Commit in repository
if (repository.Commit() > 0)
{
Console.WriteLine("E-mail updated with method 1!");
};
}
}
Method 2:
static void UpdateEmailForExistingUserMethod2()
{
var usuarioId = new Guid("f0cd6e3e-b95b-4dab-bb0b-7e6c6e1b0855");
var emailId = new Guid("3b9c2f36-659e-41e8-a1c6-d879ab58352c");
using(var usuarioRepository = new UserRepository())
{
if (!usuarioRepository.UserExists(usuarioId))
{
Console.WriteLine("User not found");
return;
}
if (!usuarioRepository.EmailExists(emailId))
{
Console.WriteLine("E-mail not found");
return;
}
// Grab only the email that I will update from the repository,
// optimizing performance
var usuarioEmail = usuarioRepository.GetEmailById(emailId);
// Updates the e-mail through a method of the e-mail entity itself
usuarioEmail.Update("updated2#email.com");
// Commit in repository
if (usuarioRepository.Commit() > 0)
{
Console.WriteLine("E-mail updated with method 2!");
};
}
}
If User is the root of the aggregate, then all modifications to the aggregate should be made by invoking a method on the root; so your "Method 1" is the correct pattern.
Specifically -- access to other entities within the aggregate is achieved by invoking a method on the root, and allowing the root to delegate the work to the internal entity if necessary.
The point is that the aggregate root(s) define the boundary between the domain model and the application.
Now, in some cases, this constraint doesn't seem to make much sense. When that happens, challenge your assumptions: are you sure that email is an entity? are you sure that entity needs to be transactionally consistent with the user entity?
For something like an email address, I would expect that the email address is going to be a value object, which can be added to a collection internal to user. So I wouldn't expect to see EmailId as an abstraction.
user.FixTypoInEmailAddress("updated#email.com", "updated1#email.com")
Do not multiply entities beyond necessity.
Is there a way to limit access to Grails domain objects across the board to the owner of the object?
For example, I can make an assert easily, but I don't want to duplicate that everywhere, or risk missing a spot.
This isn't exactly the same as multi-tenancy because it's not just a tenant ID - it may be specific business logic for different domain objects.
class MyDomain {
String name
String user
}
class MyController {
def show(Long id) {
def obj = MyDomain.get(id)
// *** How do I not do copy-paste this line in each individual controller
// that touches MyDomain?? ***
assert obj.user == CURRENT_USER
return obj
}
}
There are so many ways to handle such scenarios, as other answers are suggesting, however, I think one proper way to approach it is to use Spring Security Plugin and spring-security-acl plugin. ACL plugin will drill down into object level and helps you to control
According to doc
"The ACL plugin adds Domain Object Security support to a Grails
application that uses Spring Security."
The combination of both security core and ACL can help you accomplish what you need.
Filter might be one way, another way is to adjust your queries if possible. You would probably need to be able to search for some other criteria other than the ID. Without understanding your use case a little more, it is hard to give a better answer.
def show(String someValue) {
def currentUser = howeverYouGetYourUser
def obj = MyDomain.findByUserAndSomeValue(currentUser, someValue)
if (obj) {
// yeah!!!
} else {
// boo!!
}
}
Ideally, if you are looking for specific data for a specific user, ID isn't really the way to go.
Iam not sure if you can do it in domain level but one way is using filters. There is a filters plugin available for Grails. Keep the User in session and verify in filter for each request..
Sample filter code:
class SecurityFilters {
def filters = {
loginCheck(controller: '*', action: '*') {
before = {
if (!session.user && !actionName.equals('login')) {
redirect(action: 'login')
return false
}
}
}
}
}
and specify the filter attributes..
Here is the documentation grails.org/doc/2.2.1/ref/Plug-ins/filters.html
I need to write a service that connects to CRM, and returns with a list of all of the entity available on the server (custom or otherwise).
How can I do this? To be clear, I am not looking to return all data for all entities. Just a list of every type, regardless of whether any actually exist.
You need to use RetrieveAllEntitiesRequest
RetrieveAllEntitiesRequest request = new RetrieveAllEntitiesRequest()
{
EntityFilters = EntityFilters.Entity,
RetrieveAsIfPublished = true
};
// service is the IOrganizationService
RetrieveAllEntitiesResponse response = (RetrieveAllEntitiesResponse)service.Execute(request);
foreach (EntityMetadata currentEntity in response.EntityMetadata)
{
string logicalName = currentEntity.LogicalName;
// your logic here
}
note that you will get also system or hidden entities, like wizardpage or recordcountsnapshot
You will probably find these sections of the MSDN useful:
Customize Entity Metadata (lookout for the samples linked on that page).
Retrieve and Detect Changes to Metadata.
I have an account object that creates a user like so;
public class Account
{
public ICollection<User> Users { get; set; }
public User CreateUser(string email)
{
User user = new User(email);
user.Account = this;
Users.Add(user);
}
}
In my service layer when creating a new user I call this method. However there is a rule that the users email MUST be unique to the account, so where does this go? To me it should go in the CreateUser method with an extra line that just checks that the email is unique to the account.
However if it were to do this then ALL the users for the account would need to be loaded in and that seems like a bit of an overhead to me. It would be better to query the database for the users email - but doing that in the method would require a repository in the account object wouldn't it? Maybe the answer then is when loading the account from the repository instead of doing;
var accountRepository.Get(12);
//instead do
var accountRepository.GetWithUserLoadedOnEmail(12, "someone#example.com");
Then the account object could still check the Users collection for the email and it would have been eagerly loaded in if found.
Does this work? What would you do?
I'm using NHibernate as an ORM.
First off, I do not think you should use exceptions to handle "normal" business logic like checking for duplicate email addresses. This is a well document anti-pattern and is best avoided. Keep the constraint on the DB and handle any duplicate exceptions because they cannot be avoid, but try to keep them to a minimum by checking. I would not recommend locking the table.
Secondly, you've put the DDD tag on this questions, so I'll answer it in a DDD way. It looks to me like you need a domain service or factory. Once you have moved this code in a domain service or factory, you can then inject a UserRepository into it and make a call to it to see if a user already exists with that email address.
Something like this:
public class CreateUserService
{
private readonly IUserRepository userRepository;
public CreateUserService(IUserRepository userRepository)
{
this.userRepository = userRepository;
}
public bool CreateUser(Account account, string emailAddress)
{
// Check if there is already a user with this email address
User userWithSameEmailAddress = userRepository.GetUserByEmailAddress(emailAddress);
if (userWithSameEmailAddress != null)
{
return false;
}
// Create the new user, depending on you aggregates this could be a factory method on Account
User newUser = new User(emailAddress);
account.AddUser(newUser);
return true;
}
}
This allows you to separate the responsiblities a little and use the domain service to coordinate things. Hope that helps!
If you have properly specified the constraints on the users table, the add should throw an exception telling you that there is already a duplicate value. You can either catch that exception in the CreateUser method and return null or some duplicate user status code, or let it flow out and catch it later.
You don't want to test if it exists in your code and then add, because there is a slight possibility that between the test and the add, someone will come along and add the same email with would cause the exception to be thrown anyway...
public User CreateUser(string email)
{
try
{
User user = new User(email);
user.Account = this;
user.Insert();
catch (SqlException e)
{
// It would be best to check for the exception code from your db...
return null;
}
}
Given that "the rule that the users email MUST be unique to the account", then the most important thing is to specify in the database schema that the email is unique, so that the database INSERT will fail if the email is duplicate.
You probably can't prevent two users adding the same email nearly-simultaneously, so the next thing is that the code should handle (gracefully) an INSERT failure cause by the above.
After you've implemented the above, seeing whether the email is unique before you do the insert is just optional.